Gson ExclusionStrategy 를 활용한 annotation기반 json 출력 제외
Json Object Mapper로 널리 쓰이고 있는 Gson,
객체를 Json으로 변환하는데 클래스의 특정 필드를 출력하지 않고 싶을 때 사용 가능한 방법.
아쉽게 Gson은 필드를 json으로 출력하라는 @Expose 어노테이션만 존재할 뿐 제외하라는 @Exclude 같은 어노테이션은 아직까진 없다.
그러니까 현재까지는 어떤 필드 출력을 제외하고 싶으면 출력할 모든 필드에 @Expose 어노테이션을 적용 하고 Gson create 시에 다음처럼 @Expose 어노테이션이 없는 필드를 제외하라.. 고 설정할 필요가 있다.
[code java]
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
[/code]
그런데 대부분의 필드를 출력하고 일부만 제외하고 싶을 경우가 더 많을것 같은데.. 그 몇개 제외하자고 모든 필드에 @Expose 붙이는것도 귀찮은 일.
간단하게 @NonExpose 라든가 @Exlude 같은 어노테이션을 제공해주면 편할텐데... 우리가 생각치 못한 어떤 이유가 있겠지...
다행히 Gson 생성시 제외 조건을 설정할 수 있는 .setExclusionStrategies() 메소드를 제공한다. 그러면 다음처럼 @Exclude 어노테이션 기반 출력 제외 처리가 가능하다.
우선, 우리가 사용할 어노테이션을 정의하고...
[code java]
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}
[/code]
feildSkip 확인 코드에서 위에서 작성한 어노테이션이 있으면 제외처리되도록 Gson의 ExclusionStrategy 구현체를 작성
[code java]
public class AnnotationBasedExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(Exclude.class) != null;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
[/code]
마지막으로 Gson 객체 생성 시 위 AnnotationExclusionStrategy 를 적용.
[code java]
new GsonBuilder().setExclusionStrategies(new AnnotationBasedExclusionStrategy()).create();
[/code]
이제 제외하고 싶은 필드에 @Exclude 적용하면 끝~.
객체를 Json으로 변환하는데 클래스의 특정 필드를 출력하지 않고 싶을 때 사용 가능한 방법.
아쉽게 Gson은 필드를 json으로 출력하라는 @Expose 어노테이션만 존재할 뿐 제외하라는 @Exclude 같은 어노테이션은 아직까진 없다.
그러니까 현재까지는 어떤 필드 출력을 제외하고 싶으면 출력할 모든 필드에 @Expose 어노테이션을 적용 하고 Gson create 시에 다음처럼 @Expose 어노테이션이 없는 필드를 제외하라.. 고 설정할 필요가 있다.
[code java]
Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();
[/code]
그런데 대부분의 필드를 출력하고 일부만 제외하고 싶을 경우가 더 많을것 같은데.. 그 몇개 제외하자고 모든 필드에 @Expose 붙이는것도 귀찮은 일.
간단하게 @NonExpose 라든가 @Exlude 같은 어노테이션을 제공해주면 편할텐데... 우리가 생각치 못한 어떤 이유가 있겠지...
다행히 Gson 생성시 제외 조건을 설정할 수 있는 .setExclusionStrategies() 메소드를 제공한다. 그러면 다음처럼 @Exclude 어노테이션 기반 출력 제외 처리가 가능하다.
우선, 우리가 사용할 어노테이션을 정의하고...
[code java]
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}
[/code]
feildSkip 확인 코드에서 위에서 작성한 어노테이션이 있으면 제외처리되도록 Gson의 ExclusionStrategy 구현체를 작성
[code java]
public class AnnotationBasedExclusionStrategy implements ExclusionStrategy {
@Override
public boolean shouldSkipField(FieldAttributes f) {
return f.getAnnotation(Exclude.class) != null;
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
[/code]
마지막으로 Gson 객체 생성 시 위 AnnotationExclusionStrategy 를 적용.
[code java]
new GsonBuilder().setExclusionStrategies(new AnnotationBasedExclusionStrategy()).create();
[/code]
이제 제외하고 싶은 필드에 @Exclude 적용하면 끝~.
Trackback Address:이 글에는 트랙백을 보낼 수 없습니다
저는 GSon 을 사용안하고 Jackson을 주로 사용하는 편입니다.
exclude,include 관련 옵션은 Jackson이 더 편한것 같네요.
하지만 아무튼 가급적 exclude를 지정하기 보다는 expose(include)를 지정하는 것이 올바를 것 같습니다. 그래서 GSon도 exclude관련 기본 옵션을 두지 않은 것은 아닌가 싶습니다.
white list 방식(expose할 필드를 명시적으로 지정하는 방식)의 처리를 하는게 맞다고 보는 것이지요.
black list 방식(exclude를 설정하는 방식)으로 하게되면 신규 필드가 추가되었는데 그게 보안상 노출되면 안되는 필드라고 했을 때 해당 필드를 추가한 개발자가 명시적으로 exclusion을 하지 않으면 의도치 않게 그것을 외부에 노출시켜버리게 됩니다.
더불어 다른 문제도 있는데 객체가 양방향 관계를 맺은 상태일 때입니다. 양방향 관계를 맺은 필드를 추가한 상황에서 exclusion을 잊어버리고 표시하지 않으면 실행시점이 될 때까지 오류 상황 파악을 못하고 stackoverflow를 만나게 됩니다.
내부 API용이라면 그런대로 괜찮겠지만 특히나 아무나 데이터를 볼 수 있는 web browser와 통신하는 경우에는 white list 방식을 사용하는게 조금 귀찮더라도 보안성 더 좋을 것 같습니다.
권남님 의견 감사합니다.
말씀 주신 의도가 맞는것 같습니다.GSon 개발팀은 보안과 편의 중 보안 쪽 손을 들었을듯 합니다.... 댓글 달면서 갑자기 궁금해졌는데 @Expose 와 ExclusionStrategy를 동시에 적용하면 어느 쪽이 적용될까요? 구글 개발팀의 원래 의도대로라면 비노출 되어야 할것 같은데... 한번 확인해봐야 겠네요.