title
ํ๋ฆฌ์ฝ์ค ๊ด๋ จํ ์ ํธ ์์ค๋ ์ฌ๊ธฐ์ ์ ๋ก๋ํ๊ณ ์๋ค. ํ์ํ๋ฉด ์ฐธ๊ณ ํ๋๋ก ํ์.
์ต๊ทผ์ ๋ด๊ฐ ์์ฑํ๋ ์ฝ๋๋ค์ด ํจ์ํ ํ๋ก๊ทธ๋๋ฐ์ ์งํฅํ๋ ๊ฒ์ ๋ณด๋ฉด์, ๋์ค์ ๋น๋๊ธฐ์ ์ธ ์์
๋ค์ ์ฒ๋ฆฌํ ๋ ์์ ํค๋งค์ง๋ ์๊ฒ ๋ค ์ถ๋ค๊ฐ๋, Java์์ ๋๋ค๋ ๋ณ๋์ ์ต๋ช
ํด๋์ค์ ํจ์๋ก ์ ์ธ๋์ด ์ฌ์ฉ๋๊ธฐ ๋๋ฌธ์ ์ค๋ ๋ ๋ฉ๋ชจ๋ฆฌ ๋คํ๋ก ์ ์์ค ๋๋ฒ๊น
์ ์๋ํ ๋ ํฐ ๋ฐฉํด๊ฐ ๋๋ค๋ ์ด์ผ๊ธฐ๋ ๋ค์ด์ ์กฐ์ฌ์ค๋ฝ๊ธฐ๋ ํ๋ค. (Kotlin์ผ๋ก ๋์ด๊ฐ์ผ ํ ๋์ธ๊ฐ?)
์ด๋ฒ ์ ํธ๋ฆฌํฐ ์์ ์์ ์ป์ ๊ฒ์ ํฌ๊ฒ ์ธ ๊ฐ์ง์ด๋ค. ์ฒซ์งธ๋ javadocs์ ๋ํ ์ง์์ด ํ๋ ๋์๋ค๋ ๊ฒ, ๋์งธ๋ '์ด ํธํ๊ณ ์ข์ ๊ฑธ ์ ์ง์ ์ ํ๊ณ ์์์ง?' ๋ผ๋ ๊นจ๋ฌ์, ๊ทธ๋ฆฌ๊ณ ๋ง์ง๋ง์ ์ง์์ ์ผ๋ก ๊ด๋ฆฌ๋๋ ์์ค์ ๋ฐ์ ๊ฐ๋ฅ์ฑ์ด๋ค.
javadocs
์ ํธ๋ฆฌํฐ์ ๋ํ ์ค๋ช
์ ์์ธํ ์์ฑํ๋ ค๋ค๋ณด๋ javadocs ๋ฌธ๋ฒ์ ์์ฐ์ค๋ฝ๊ฒ ๊ด์ฌ์ด ๊ฐ๋ค. ๊ธฐ์กด์ ์ธ๋ถ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฏ์ด๋ณด๋ฉด @see, {@link} ๋๋ <pre>์ {@code}์ ์กฐํฉ์ ๋ณผ ์ ์์๋๋ฐ, ์ด๋ฒ์ ์ฐ์ฐํ {@snippet}๋ ์๊ฒ ๋์๋ค.1
'ํ ๋ฌ ํ์ ๋ค์ ๋ณด๋๋ผ๋ ์ดํดํ๊ณ ์ฌ์ฉํ ์ ์๋๋ก' ์์ฑํ๋ ค ๋
ธ๋ ฅํ๋๋ฐ ์๊ฐ๋ณด๋ค ์ฝ์ง ์๋ค. IDE์์ ๋ณด๋ฉด ์ข ๋ ๊น๋ํด๋ณด์ด๊ธด ํ๋๋ฐ, ์ฅํฉํ๊ฒ ์ธ๋งํผ ๋๋จํ ๋ด์ฉ์ด ํ์ํ ๊ธฐ๋ฅ์ ์๋์ง๋ง, ๊ทธ๋ ๋ค๊ณ ์ค๊ธ๋ง ๋จ๊ฒจ๋์๋ ์ฒ์์ ๋ชฉํ์ ๋ถํฉํ์ง ์์์ ๊ณ ๋ฏผ์ ๋ง์ด ํ๋ค. ๋ณดํต ์คํ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ณด๋ฉด ํ์ฉ ์์ ํ๋์ฉ์ ๊ผญ ๊ฐ์ถ๊ณ ์์ด์ ๋๋ ๊ฐ๋จํ ์์ฑํด๋ดค๋ค. gist ์์ค ๋งํฌ 2

์ ํธ๋ฆฌํฐํ
์ด๋ง์ด๋งํ๊ฒ ๊ฑฐ๋ํ ์คํ์์ค ์ํ๊ณ๋ ์ด๋ฏธ ์ฐ๋ฆฌ ์ถ ๊น์ ๊ณณ๊น์ง ์นจํฌํ ์ง ์ค๋๋ค. ์์ ์ ํธ๋ฆฌํฐ ์์ค์กฐ์ฐจ ๊ทธ ํ๊ธ๋ ฅ์ ๊ฑฐ๋ํ ์ ์๋ค๋ ์ฌ์ค. 1. kik-left-pad-and-npm 2. wiki/Npm_left-pad_incident ์์ ํจ์๋ค์ด ๋ง๋ฌผ๋ ค ๊ฑฐ๋ํ ์์คํ ์ ๊ตฌ์ฑํ๋ ๋ฉ์ง ๊ฒฝ์ฐ๋ ์์ง๋ง, ๊ฒฐ๊ตญ ์ ์ฌ์ ์์ ์์์ ์ ์ฌ์ฉํ๋ ๊ฒ์ด ์ฐธ ์ด๋ ค์ด ์ผ์ธ ๊ฒ ๊ฐ๋ค. ํ์ธ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๋๋ ๋์ฑ.
๊ทธ๋ฐ ์ ์์ ๋ด๊ฐ ๋๋ง์ ์ ํธ๋ฆฌํฐ ํด๋์ค๋ฅผ ๋ง๋ค๊ณ ๊ด๋ฆฌํ๋ ๊ฒ์ ๊ฐ์ธ์ ์์ฐ์ฑ ์ธก๋ฉด์์ ๋งค์ฐ ํจ๊ณผ์ ์ด๋ผ๊ณ ์๊ฐํ๋ค. ๋ฑ ๋ ์ ๋์ ์์ค์ ๊ฐ์ง ์ฌ๋์ด ๋ง๋ ์ด ์ฝ๋๋, ๋ด๊ฐ ์ดํดํ ์ ์๋ ๊ธฐ๋ฅ์ ๋ด๊ฐ ์ฌ์ฉํ๊ธฐ ํธํ ๋ฐฉ์์ผ๋ก ์ ๊ตฌํํด๋์์ผ๋๊น.
๋ด๊ฐ ์์ฑํ Parser๋ ๊ธฐ์กด String.split์์ ํ์ฉํ๋ trailingDelimiter์ ๋ํ ๊ฒ์ฌ๋ฅผ ํฌํจํด์ ๋ณด๋ค ์๊ฒฉํ ํ์ ๊ฒ์ฌ๋ฅผ ์ํํ๋, parse() ๋ฉ์๋์ ์ค๋ฒ๋ก๋ฉ์ผ๋ก ๋ฐ์ดํฐ์ ๋ณํ ํ๋ฆ์ด ์ ์ฐํ๊ฒ ์ด์ด์ง ์ ์๋๋ก ์ ๊ฒฝ์ผ๋ค.
package example.util;
import java.util.List;
import java.util.regex.Pattern;
/**
* ์ฃผ์ด์ง ๊ตฌ๋ถ์๋ฅผ ๊ธฐ์ค์ผ๋ก ์์๋ฅผ ์งํค๋ฉฐ ํ ํฐ์ ๋ถํ ํฉ๋๋ค.
* <pre>{@code
* var parser = new Parser(',');
* console.printLine("๊ฒฝ์ฃผํ ์๋์ฐจ ์ด๋ฆ์ ์
๋ ฅํ์ธ์.(์ด๋ฆ์ ์ผํ(,) ๊ธฐ์ค์ผ๋ก ๊ตฌ๋ถ)");
* var carNames = parser.parse(console.readLine());
* }</pre>
* 2๊ฐ ์ด์์ ๊ตฌ๋ถ์๋ ์ฌ์ฉํ ์ ์์ต๋๋ค. ๊ฐ๊ฐ์ ๊ตฌ๋ถ์๋ฅผ ๋์ดํด์ ์์ฑํ์ธ์.
* {@snippet :new Parser(',', ':').parse("1,2:3"); // {1, 2, 3} // @highlight regex="'.'" }
*/
public final class Parser {
private final Pattern pattern;
public Parser(int... delimiter) {
String quotes = java.util.Arrays.stream(delimiter)
.mapToObj(Parser::toRegexQuote)
.collect(java.util.stream.Collectors.joining());
pattern = Pattern.compile(String.format("[%s]", quotes));
}
private static String toRegexQuote(int c) {
return Pattern.quote(Character.toString(c));
}
public List<String> parse(String input) {
if (input.isEmpty()) {
return List.of();
}
checkTrailingDelimiter(input);
return List.of(pattern.split(input));
}
public <T> List<T> parse(String input, java.util.function.Function<String, T> converter) {
return parse(input).stream().map(converter).toList();
}
private void checkTrailingDelimiter(String input) throws IllegalArgumentException {
var s = String.valueOf(input.charAt(input.length() - 1));
if (pattern.matcher(s).matches()) {
throw new IllegalArgumentException();
}
}
}
์ง์์ ์ธ ๊ด๋ฆฌ ๋ฐ ๊ฐ๋ฐ
๋ฐ๊ฟ ์ผ์ด ์์๊ฑฐ๋ผ ์๊ฐํ๋ ๊ณณ์ ๋ณ๊ฒฝํ ์ผ์ด ์๊ธฐ๊ณ , ์๋ฒฝํ๋ค๊ณ ์๊ฐํ ์ฝ๋์ ๊ฒฐ์ ์ด ๋ณด์ด๋ ๋๊ฐ ์๊ธด๋ค.
๋ด๊ฐ ValueRules๋ผ๋ ์ ํธ์ ์์ฑํ๊ฒ ๋ ๊ณ๊ธฐ๋, ์์ฃผ ํ์ํ๊ฒ ๋๋ ํน์ ํ ๊ฒ์ฆ ํจํด๋ค์ '๋ธ๊น'ํด์ ํด๊ฒฐํ๊ณ ์ถ๋ค๋ ์ํํ ๋ง์ ๋๋ฌธ์ด์๋ค. ์กฐ๊ฑด๋ฌธ๊ณผ try-catch ํจ์๋ฅผ ๋ง๋ค๋ฉด ๋๋๋ฐ ๊ทธ๊ฒ ๊ท์ฐฎ์๋๋ณด๋ค ๐ ํ์ง๋ง ๋ง์ ์์
์ ์์ํ๋ ์์ด๋์ด๊ฐ ์์์, ์ ๋ค๋ฆญ๊ณผ ํจ์ํ ์ธ์๋ฅผ ํ์ฉํด์ ๋ชจ๋ ํ์
์ ์ง์ํ๋๋ก ๊ตฌํํ๋ค.
์ด๊ธฐ ์์ด๋์ด๋ ๊ทธ๋ฅ RuntimeException์ ์์ํ๋ ์ปค์คํ
์์ธ๋ฅผ ๋์ง๋ ๊ฒ์ด์๋๋ฐ, ๋ง์ ์ ๋ค๋ฆญ์ผ๋ก ๋ง๋ค๊ณ ๋ณด๋ ์ ํธ์ด๋ผ null ๊ฒ์ฌ๋ ํด์ผํ ๊ฒ ๊ฐ๊ณ , Objects.requireNonNull์ ํ์ฉํด์ NullPointerExceptio๋ ๋์ง๋๋ก ๋ง๋ค์๋ค. ๋น์์๋ ์ด๋ ๊ฒ ํ๋ฉด ์ฌ์ฉ์ ์
์ฅ์์ ๋ ๊ฐ๋ฅผ ๊ตฌ๋ถํด์ ์ฒ๋ฆฌํ ์ ์์ผ๋ ์ข์ ๊ฒ ๊ฐ๋ค๊ณ ์๊ฐํ๋ค.
@FunctionalInterface
public interface CanValidate<T> {
void validate(T value) throws NullPointerException, InvalidValueException;
}
public record OfString(java.util.function.Predicate<String> assertion) implements CanValidate<String> {
@Override
public void validate(String value) throws NullPointerException, InvalidValueException {
java.util.Objects.requireNonNull(value);
InvalidValueException.throwsIf(assertion.test(value), this.getClass());
}
}
ํ์ง๋ง ๋ง์ ์ค์ ๋ก ์ฌ์ฉํด๋ณด๋ ๋ถํธํ ์ ์ด ๋๊ปด์ก๋ค. ์ปจํธ๋กค๋ฌ ์์ค์์ ๊ฐ์ ๊ฒ์ฆ ์คํจ์ ๋ฐ๋ฅธ ์์ธ๋ฅผ IllegalArgumentException์ผ๋ก ๊ณ ์ ํ๊ณ catchํด์ ์ฌ์ํํ๊ณ ์์๋ค๋ณด๋ ๋ค๋ฅธ ์์ธ์๋ catch ๊ตฌ๋ฌธ์ ์ถ๊ฐํด์ผ๋ง ํ๋ค. ํธํ๋ ค๊ณ ์ฌ์ฉํ๋ ์ ํธ์ด ์คํ๋ ค catch ๊ตฌ๋ฌธ์ ๊ธธ๊ฒ ๋ง๋ ๊ฒ์ด๋ค. ๊ทธ๋์ ์์ธ๋ฅผ custom ์์ธ ํ๋๋ก ํตํฉํ๊ณ , checkedException์ผ๋ก ๋ฐ๊ฟจ๋ค. ๋๋ ์ด๋ฐ ๋ฐฉ์์ด ๋ ํธํ๊ฒ ๋๊ปด์ก๋ค.
public record Stock(String name, int price, int quantity) {
private static final ValueRules.OfInteger priceRule = new ValueRules.OfIntegerinteger -> integer >= 100 && integer % 10 == 0);
public Stock {
try {
ValueRules.OF_NON_BLANK.validate(name);
priceRule.validate(price);
ValueRules.OF_POSITIVE.validate(quantity);
} catch (InvalidValueException e) {
throw GlobalExceptions.INVALID_ARGUMENTS.exception();
}
}
}
Java API ๋๋ค์๋ ์ฌ์ฉ์์๊ฒ try-catch๋ฅผ ๊ฐ์ ํ์ง ์๋๋ก ๋ฐํ์ ์์ธ๋ฅผ ๋ง์ด ์ฌ์ฉํ๋ค. ์ต๊ทผ์๋ Reflection API ์ ๋์์๋ง checkedException์ ๋ณธ ๊ฒ ๊ฐ๋ค. ๊ทธ๋์ ๋์ฑ ์ปค์คํ ์ ํธ์ด ์ค์ํ๊ฒ ๋๊ปด์ง๋ค. ์ฝ๋๊ฐ ๋ถ๋ช ํ๊ฒ ์๋ชป๋ ๊ฒ์ด ์๋๋ผ๋ฉด, ํ์ฉ๋ ์์ ์์์ ๋ด๊ฐ ์ถ๊ตฌํ๋ ๋ฐฉํฅ์ ์ฝ๋๋ฅผ ์ฌ์ฉํ๋ ๊ฒ์ ์์ฐ์ฑ ํฅ์์ ํฐ ๋์์ด ๋ ํ ๋๊น.
Footnotes
-
ํ์ฌ ํ๋ก์ ํธ์ ์์ค์ ๋ํ ์ฐธ์กฐ๋ฅผ ์๋์ผ๋ก ๋งํฌ๋ก ๋ง๋ค๊ณ , ์ธ๋ถ ํ์ผ์ ์์ฑํ ์ฝ๋ ๋ด์ฉ์ ๋ค์ํ ๋ฐฉ์์ผ๋ก ๋ถ๋ฌ์ค๊ณ , ๋ถ๋ฌ์จ ํ์๋ ๊ฐ์กฐํ๋ ๋ฑ์ ์์ ์ ์ฝ๊ฒ ํ ์ ์๋๋ก ์ง์ํ๋ค. ์์ธํ ๋ด์ฉ์ ์ค๋ช ์ ์ฐธ๊ณ ํ์. โฉ
-
์ค๊ฐ์
{@snippet}๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์๋code-snippetํด๋๋ฅผ ํ์ฉํด์ ์ฝ๋๋ฅผ region ๋ณ๋ก ๊ฐ์ ธ์ค๋ ์์ผ๋ก ๋ฌธ์๋ฅผ ์์ฑํ์๋๋ฐ, ๊ดํ ํ์ผ์ด ๋ถ๋ฆฌ๋๋ ๊ฒ๋ ์ข์ง ์์ ๊ฒ ๊ฐ์์ ๋ค์ ํ๋๋ก ํฉ์ณค๋ค. โฉ