-
Notifications
You must be signed in to change notification settings - Fork 305
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[MVC 구현하기 - 2, 3단계] 에어(김준서) 미션 제출합니다. #73
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이번에는 @ResponseBody
까지 구현을 해주셨네요 ㅎㅎ
전 단계에서 잘 만들어놔서, 코드 리뷰를 하는데도 너무 편했습니다! 가독성이 좋은 코드 good 👍
테스트 또한, 이렇게 꼼꼼히 진행해줬네요! 최곱니다
몇가지 코멘트 남긴 부분 확인해주시고, 다시 리뷰 요청해주시면 바로 머지 하겠습니다! 요번에도 많이 배웠네요!
public AbstractMessageConverterHandler() { | ||
this.messageConverters = new ArrayList<>(); | ||
this.messageConverters.add(new StringHttpMessageConverter()); | ||
this.messageConverters.add(new ObjectHttpMessageConverter()); | ||
} | ||
|
||
protected <T> void writeWithMessageConverter(T value, HttpServletResponse response) throws IOException { | ||
HttpMessageConverter<?> converter = messageConverters.stream() | ||
.filter(mc -> mc.canWrite(value.getClass())) | ||
.findFirst() | ||
.orElseThrow(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
StringHttpMessageConverter
, ObjectHttpMessageConverter
를 add 해주는 순서에 따라 결과가 달라질 수 있겠네요!
지난 미션 때, ControlerAdvice
에서 ExceptionHandler
를 Exception별로 처리해주는 로직을 구현했었는데, 딱 이와 같은 상황을 경험했어요!
그 때, 에어와 같은 방식으로, 먼저 만나는 Exception에 속하면, 해당하는 ExceptionHandler로 처리되게끔..
그 때, 개인적으로 실수를 유발할 수 있는 그런 코드라고 생각이 들어서 의견을 남겨봐요! 다른 개발자가 이 코드를 봤을 떄, 이 순서를 고려하지 않고 새로운 Converter를 추가할 가능성이 있는?
ObjectHttpMesageConverter에서 String.class 를 입력받았을 때 예외 처리를 진행할 수도 있겠지만, 그 방식도 다른 Converter가 추가되었을 때 항상 고려해야 하는 포인트라 생각되서 썩 내키지 않네요..
이야기가 길어졌는데, 코드의 수정을 바라기 보다는 그냥 제 요런 저런 생각을 적어봤네요 ㅎㅎ. 지금의 코드도 충분하다 생각합니다!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와.. ExceptionHandler를 구현하셨었군요👍🏻
완태의 의견을 듣고보니 정말 실수를 유발할 수 있는 부분인 것 같아요!
if (model.size() == 1) { | ||
return objectMapper.writeValueAsString(firstData(model)); | ||
} | ||
return objectMapper.writeValueAsString(model); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
오호 저는 이 요구사항을 놓쳤는데, 잘 구현해주셨네요!
|
||
@Target({ElementType.TYPE, ElementType.METHOD}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface ResponseBody { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
대단하십니다... 하나씩 기능이 추가되는..!
@RequestMapping(value = "/json/string", method = RequestMethod.GET) | ||
@ResponseBody | ||
public String jsonString(HttpServletRequest request, HttpServletResponse response) { | ||
return "ok"; | ||
} | ||
|
||
// @ResponseBody & Object를 리턴하는 컨트롤러 | ||
@RequestMapping(value = "/json/object", method = RequestMethod.GET) | ||
@ResponseBody | ||
public User jsonObject(HttpServletRequest request, HttpServletResponse response) { | ||
return new User(1L, "air", "1234", "air.junseo@gmail.com"); | ||
} | ||
|
||
// ModelAndView를 통해 JsonView를 렌더링하는 컨트롤러(모델 1개) | ||
@RequestMapping(value = "/json/view", method = RequestMethod.GET) | ||
public ModelAndView jsonView(HttpServletRequest request, HttpServletResponse response) { | ||
ModelAndView modelAndView = new ModelAndView(new JsonView()); | ||
User user = new User(1L, "air", "1234", "air.junseo@gmail.com"); | ||
modelAndView.addObject("user", user); | ||
return modelAndView; | ||
} | ||
|
||
// ModelAndView를 통해 JsonView를 렌더링하는 컨트롤러(모델 2개) | ||
@RequestMapping(value = "/json/view2", method = RequestMethod.GET) | ||
public ModelAndView jsonView2(HttpServletRequest request, HttpServletResponse response) { | ||
ModelAndView modelAndView = new ModelAndView(new JsonView()); | ||
User user1 = new User(1L, "air", "1234", "air.junseo@gmail.com"); | ||
User user2 = new User(1L, "air", "1234", "air.junseo@gmail.com"); | ||
modelAndView.addObject("user1", user1); | ||
modelAndView.addObject("user2", user2); | ||
return modelAndView; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
감탄 x1000000000000000000!
으마으마 하다..ㅋㅋㅋ
@Configuration | ||
public class ReturnValueHandlerConfiguration { | ||
|
||
@Bean | ||
public ViewReturnValueHandler viewReturnValueHandler() { | ||
return new ViewReturnValueHandler(); | ||
} | ||
|
||
@Bean | ||
public JsonReturnValueHandler jsonReturnValueHandler() { | ||
return new JsonReturnValueHandler(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
잘 만들어두니 진짜 잘 쓰이네요 ㅎㅎㅎ
|
||
import jakarta.servlet.http.HttpServletResponse; | ||
|
||
public abstract class AbstractMessageConverterHandler implements ReturnValueHandler { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
코드를 보면서 AbstracMessageConverterHandler
가 ReturnValueHandler
의 구현체라는 부분이 클래스의 이름으로는 예측되지 않았습니다!
ReturnValueHandler를 다른 방식과 동일하게 suffix로 두는 것은 어떨까요? (뭔가 에어도 고민을 했을것 같은데, 너무 길어지긴 하네요! 이건 개인 취향 차라 생각합니다~)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
저도 고민하다가 너무 길어지는 것 같더라구요 ㅠㅠ
그런데 막상 바꿔보니 그렇게 길지 않은 것 같기도 하네요 👍🏻
오히려 바꾸니까 더 명확한 것 같아요!
SonarCloud Quality Gate failed. |
코멘트 주신 #73 (comment) 이 부분을 보고 나름대로 어떻게 할까 고민을 해봤어요! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와.. 이걸 또 다른 에어만의 새로운 해결책을 찾았네요!
어노테이션을 자유자재로 다뤄버리는... 최고네요! ㅎㅎ
요번 미션은 이만 머지하지겠습니다~
@Retention(RetentionPolicy.RUNTIME) | ||
@Target(ElementType.TYPE) | ||
public @interface Order { | ||
int value() default Integer.MAX_VALUE; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
와 이제는 어노테이션의 마술사네요 진짜 ... 대박입니다.
이걸 어노테이션을 붙여서 해결하다니.. 대박이네요 진짜 ㅎㅎ.
요렇게 구현하니 개발자가 실수할 일도 없겠네요 !!
안녕하세요 완태! 저번 리뷰 너무 좋았어요 👍🏻
2단계와 3단계를 구현해봤습니다! 이번 리뷰도 잘 부탁드립니다 ㅎㅎ
요구사항에 맞게 JsonView도 구현했지만, 스프링은 어떻게 처리할까? 궁금증이 들어 찾아보고 스프링이 처리하는 방식도 한 번 도전해봤습니다!
스프링의 경우
@ResponseBody
가 붙어 있는 핸들러는 view를 사용하지 않고, 메세지 컨버터를 사용하여 직접 바디에 응답을 적어주더라고요.구조가 복잡해서 제대로 파악한지는 모르겠지만 나름 참고하여 구현해봤습니다!
❓요구사항처럼 JsonView를 통해 json을 처리해도 될 것 같은데, 왜 스프링은 json 처리를 할 땐, view를 사용하지 않는 걸까요?? 나눠야하는 이유가 있을까요?
구현 내용
ModelAndView(new JsonView())
인 경우 -> view에서 rendering(test: GET /api/user?account=gugu)@ResponseBody
가 붙어있고 반환값이 Object인 경우 -> 반환객체를 json으로 변환후 적어줌(test: GET /api/user/v2?account=gugu)@ResponseBody
가 붙어있고 반환값이 String인 경우 -> 반환값을 바로 적어줌(test: GET /api/ok)추가적으로 테스트를 어떻게 작성해야할지가 어려웠어요.. 나름 작성해봤지만 완태가 한 번 봐주시면 좋겠네요!
혹시 궁금한 점 있으시면 언제든지 연락주세요! 이번에도 잘 부탁드려요 ㅎㅎ 🙏🙏🙏