Jackson ๋ง์คํน ์ฒ๋ฆฌ
โ ๊ฐ์ธ์ ๋ณด์ ๊ธฐ์ ์ ๊ด๋ฆฌ์ ๋ณดํธ์กฐ์น ๊ธฐ์ค ์ 10์กฐ
์ ๋ณดํต์ ์๋น์ค ์ ๊ณต์๋ฑ์ ๊ฐ์ธ์ ๋ณด ์ ๋ฌด์ฒ๋ฆฌ๋ฅผ ๋ชฉ์ ์ผ๋ก ๊ฐ์ธ์ ๋ณด์ ์กฐํ, ์ถ๋ ฅ ๋ฑ์ ์ ๋ฌด๋ฅผ ์ํํ๋ ๊ณผ์ ์์ ๊ฐ์ธ์ ๋ณด๋ณดํธ๋ฅผ ์ํ์ฌ ๊ฐ์ธ์ ๋ณด๋ฅผ ๋ง์คํนํ์ฌ ํ์์ ํ ์กฐ์น๋ฅผ ์ทจํ ์ ์๋ค.
์์ ๊ฐ์ด ๊ฐ์ธ์ ๋ณด ๋๋ ๋ณด์ ์ด์๋ก ์ธํ์ฌ ์ผ๋ถ ๋ฏผ๊ฐํ ๋ฐ์ดํฐ ํญ๋ชฉ์ ์ ์ฒด๊ฐ ์๋ ์ผ๋ถ๋ง์ ํ์ํด์ผํ ์๊ตฌ์ฌํญ์ด ์์ ์ ์๋ค. Jackson AnnotationIntrospector ์ด์๋ฅผ ๊ฒฝํํ ๊น์ AnnotationIntrospector๋ฅผ ์ฌ์ฉํ์ฌ REST API์์ ์๋ต๋๋ ์ผ๋ถ ํ๋๋ฅผ ๋ง์คํนํ๋ ๋ฐฉ๋ฒ์ ์ดํดํด๋ณด๋๋ก ํ์.
Annotation ๊ธฐ๋ฐ ๋ง์คํน ์ฒ๋ฆฌ
๊ธฐ๋ณธ์ ์ผ๋ก ๋ณ๋์ Getter ํจ์๋ก ๋ง๋ค์ด์ ๋ง์คํนํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ ํ๋๋ฅผ ๋ง๋ค์ด๋ด๋ ๋๋ค. ๊ทธ๋ฌ๋, ๋ง์คํน์ ์ํ ์ด๋ ธํ ์ด์ ์ ๋ง๋ค๊ณ AnnotationIntrospector๋ฅผ ํ์ฅํด์ ์ด๋ ธํ ์ด์ ์ด ์ ์ธ๋ ํ๋์ ๋ํด์ ๋ง์คํน๋ ๊ฒฐ๊ณผ๋ก ์ง๋ ฌํ(Serialize)๋ฅผ ์ํํ๋๋ก ์์ฑํ๋ฉด ๋ง์คํน ๋์ด์ผํ๋ ํญ๋ชฉ์ ๋ฐ๋ผ์ ๋ค์ํ ๋ง์คํน ํจํด์ ์ ๋ต์ ์ผ๋ก ์ ์ฉํ ์ ์๋ค.
@Target({ElementType.ANNOTATION_TYPE, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotation
public @interface MaskedField {
String expression() default "****";
MaskedType type() default MaskedType.COMMON;
String[] fields() default {}; // NOTE: If metadata
}
public class MaskedFieldAnnotationIntrospector extends NopAnnotationIntrospector {
@Override
public Object findSerializer(Annotated annotated) {
MaskedField annotation = annotated.getAnnotation(MaskedField.class);
if (annotation != null) {
return MaskedFieldSerializer.class;
}
return null;
}
public static class MaskedFieldSerializer extends StdSerializer<Object> implements ContextualSerializer {
private final boolean isMask;
private final MaskedField annotation;
public MaskedFieldSerializer(MaskedField annotation, boolean isMask) {
super(Object.class);
this.isMask = isMask;
this.annotation = annotation;
}
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
// NOTE: MaskedType ์ ๋ฐ๋ฅธ ๋ง์คํน ํจํด ๊ตฌํ์ ์๋ต
ObjectMapper mapper = (ObjectMapper) gen.getCodec();
String s = mapper.writeValueAsString(value);
if (isMask) {
JSONParser parser = new JSONParser(DEFAULT_PERMISSIVE_MODE);
try {
Object o = parser.parse(s);
if (o instanceof JSONAwareEx ex) {
DocumentContext doc = JsonPath.parse(ex.toJSONString());
Map json = doc.json();
for (String field : annotation.fields()) {
if (json.containsKey(field)) {
doc.set(field, annotation.expression());
}
}
gen.writeRawValue(doc.jsonString());
} else {
gen.writeString(annotation.expression());
}
} catch (ParseException e) {
e.printStackTrace();
}
} else {
gen.writeRawValue(s);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
MaskedField maskedField = null;
if (beanProperty != null) {
maskedField = beanProperty.getAnnotation(MaskedField.class);
}
return new MaskedFieldSerializer(maskedField, true);
}
}
}
@Bean
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = Jackson2ObjectMapperBuilder.json().build();
AnnotationIntrospector introspector = objectMapper.getSerializationConfig().getAnnotationIntrospector();
AnnotationIntrospector annotationIntrospector = AnnotationIntrospector.pair(introspector, new MaskedFieldAnnotationIntrospector());
objectMapper.setAnnotationIntrospector(annotationIntrospector);
return objectMapper;
}
- ๋ค์ํ ๋ง์คํน ํจํด ๋์์ ์ํ MaskedType Enum ๋ง๋ค๊ธฐ
- NopAnnotationIntrospector ๋ฅผ ํ์ฅํ MaskedFieldAnnotationIntrospector ํด๋์ค ์์ฑํ๊ธฐ
- objectMapper์ AnntationIntrospectorPair๋ก MaskedFieldAnnotationIntrospector ๋ฑ๋กํ๊ธฐ
๋ง์คํน ์ ๋ต
์ด๋ฆ๊ณผ ์ฃผ๋ฏผ๋ฑ๋ก๋ฒํธ ๊ทธ๋ฆฌ๊ณ ํด๋ํฐ ๋ฒํธ์ ๊ฐ์ด ๋ฏผ๊ฐํ ๋ฐ์ดํฐ์ ๋ํด์๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๋ง์คํน์ด ๊ณ ๋ ค๋์ด์ผํ๋ค. ๋ง์คํน ์ ๋ต์๋ ๋ค์์ N๊ฐ์ ๋ฌธ์ ๋๋ ๋ฐ์ดํฐ ํ์์ ๋ฐ๋ผ ์ค๊ฐ์ N๊ฐ์ ๋ฌธ์๋ฅผ ๋ณํ(asterisk)๋ก ์นํํ๋ค. ๊ฒฝ๊ธฐ๋ํ๊ต ์ ์ฐ์ ๋ณด์์ ๊ฐ์ธ์ ๋ณด ๋ ธ์ถ ์กฐ์น ๋ฐฉ๋ฒ ์๋ด์์ ์ฌ๋ฌ๊ฐ์ง ์์๋ฅผ ์ ๋ํ๋ด๊ณ ์๋ ๊ฒ ๊ฐ๋ค.
๋ฏผ๊ฐํ ๋ฐ์ดํฐ์ ๋ํ ๋ง์คํน ์ฒ๋ฆฌ๋ฅผ ๋ฐ๋์ ์ ํ๋ฆฌ์ผ์ด์ ์๋ฒ์์ ์ด๋ฃจ์ด์ ธ์ผํฉ๋๋ค. ๋ํ, ์๋ก ๋ค๋ฅธ ์์คํ ์์ ๊ฐ์ธ์ ๋ณด๋ฅผ ๊ณต์ ํ๋๋ฐ ๊ฐ ์์คํ ์์์ ๋ง์คํน ์ ๋ต์ด ๋ค๋ฅด๋ค๋ฉด ์ด๊ฒ๋ ๊ฐ์ธ์ ๋ณด ์ฒ๋ฆฌ์ ๋ณดํธ ์กฐ์น์ ๋ํ ๋ฌธ์ ์ ์์ง๊ฐ ์์ ์ ์์ต๋๋ค.
๊ทธ๋ฌ๋ฉด, ์ ํ๋ฆฌ์ผ์ด์ ๋ก๊ทธ์ ํฌํจ๋ ์ ์๋ ๋ฏผ๊ฐํ ์ ๋ณด๋ ์ด๋ป๊ฒ ๋ง์คํน ํด์ผํ์ง? ๐ค