내가 맡은 부분은 서버 즉 백엔드 부분이므로, 설정한 언어에 맞게 Error Message를 클라이언트로 전달해야 했다.
기존 구 어드민 사이트에서는 .properties 파일에 Message들을 저장했지만 나는 DB에 저장하여 관리키로 함.
전략이라 할 것도 없지만.. 어쨌든 내가 가고자 하는 방향에 맞는 전략은 아래와 같다.
(내 전략의 방향이 맞는지, 그리고 이것을 구현한 방법이 맞는지 아직도 의문이다. 따라서 혹시라도 이것을 보시고 더 좋은 방법이나 틀린 부분이 있다고 느끼시는 분들이 있다면 Comment로 남겨주시면 제가 그것을 감사히 읽어보고 수정하도록 하겠습니다.)
- 에러 메시지는 DB에 저장
- 에러 메시지를 클라이언트로 전달할 때는 Caching되어진 데이터를 전달하고, 데이터는 locale과 messageKey 값으로 찾음
- Exception은 @RestControllerAdvice로 지정한 class에서 처리
@RestContorllerAdvice로 설정된 전역 ExceptionHandler가 클라이언트에서 전달한 Accept-Language Header값에 맞는 Exception Message로 전달하는 역할을 담당한다. 각 메소드는 Exception에 대한 MessageKey를 가지고 있어 Caching되어진 Exception Data에서 MessageKey와 Locale 정보에 맞는 Exception Message를 불러와서 Client로 전달한다. 단, 어떤 Exception 은 구체화된 에러 메시지를 내려줄 필요성이 있기에 이런 경우에는 exception을 throw할 때 overriding한 CustomException의 messageKey를 set해서 전달한다. 아래 로직은 @ExceptionHandler가 Exception을 Client에 전달하기 전에 수행하는 작업에 대한 내용이다.
@RestControllerAdvice
@Slf4j
public class AdminExceptionHandler {
@ExceptionHandler(ForbiddenRequestException.class)
public ResponseEntity<?> handleForbiddenRequestException(ForbiddenRequestException e, Locale locale) {
log.error(ExceptionUtils.getExceptionMessage(e));
String messageKey;
if (e.getMsgKey() != null) {
messageKey = e.getMsgKey();
} else {
messageKey = "forbidden.request";
}
return ApiMessage.builder().errorMessage(resource.getMessage(CacheKeys.builder().messageKey(messageKey).locale(locale.toString()).build())).status(e.getStatus()).build().toEntity();
}
}
그리고 아래 부분은 DefaultLocale 값을 설정하고, Accept-Language 값을 Locale로 설정하기 위해 구현한 내용이다.
나는 CookieLocaleResolver를 사용했다. 아래 설정을 마치면 @ExceptionHandler로 지정한 메소드로 전달한 Locale값이 Accept-Language 헤더값으로 잘 전달된다.
artifacts 파일에 있는 appspec.yml과 scripts/** 는 CodeBuild가 .zip으로 패키징 할 때 jar파일에 appspec.yml, start.sh를 포함시킨다. CodeDeploy는 S3에 있는 .zip파일 압축을 풀어서 appspec.yml파일을 참고해서 배포한다.
우선, 자바 기본책을 다시 정독하기 위해 자바의 정석 3rd edition을 새로 구입하여 두세번 읽어보기로 다짐.
기본도 정확히 모르는 주제에 무슨 개발을 하겠다고 설쳐댄건지.
내가 정확하게 다 알 수 있다고 자신있게 말하려면
최소한 기본책에 나온 어떤 내용에 대한 질문에도 짧고 간결하게 답할 수 있는 정도. (구구절절? -> NO)
그 정도가 되었다는 자신이 생겼다면 지금 사두고 잘 읽지 못하고 있는 '모던 자바 인 액션'을 읽기로 하겠다.
Anyway,
지금 어쨌든 회사에서 당장 개발해야 될 것들이 있기 때문에 책이 오기 전까지 Spring이 무엇인지, 그리고 동작원리는 어떻게 되는지 짧게라도 짚고 개발을 해야 죄책감이 아주 조금이라도 덜 것 같기에 내용이 잘 정리된 포스팅을 찾고, 그 내용을 기반으로 내 방식대로 다시 정리해보았다. 아래 나온 모든 내용은 맨 하단에 나온 출처로 부터 나온 내용!
스프링이란?
IoC와 AOP를 지원하는 경량의 컨테이너
컨테이너란?
객체의 생성, 관리를 담당하고, 객체를 운용하는 데 필요한 기능을 제공한다.
스프링에서 컨테이너 역할을 하는 것은 ?
BeanFactory
ApplicationContext (BeanFactory 상속)
두 컨테이너의 차이점은?
BeanFactory
applicationContext.xml (스프링 설정 파일) 에 등록된 bean 객체를 생성 및 관리
클라이언트로부터 요청이 들어올 때만 객체를 생성한다.
ApplicationContext
BeanFactory의 객체 생성 및 관리 + 트랜잭션 관리 / 메시지 기반의 다국어처리 지원
컨테이너가 구동되는 시점에 bean에 등록된 클래스를 객체화 한다.
GenericXmlApplicationContext -> ApplicationContext를 구현한 대표 클래스
스프링의 특징
POJO(Plain old java object)
예전에는 자바로 웹애플리케이션을 개발하려면 Servlet클래스를 상속받아 구현해야 했는데 이 Servlet이 POJO가 아니었다. 그런데 스프링을 사용하면 POJO만으로 웹 애플리케이션을 구축할 수 있게 되었다. Servlet클래스를 모두 추상화하여 라이브러리로 들어갔기 때문에. XML이나 다른 설정으로 Servlet을 이요하면 된다.
IoC(Inversion of Control)
객체 생성을 자바 코드로 직접 처리하는 것이 아니라, 컨테이너가 대신 처리한다.
제어권이 사용자가 아니라 프레임워크에 있다.
객체와 객체 사이의 의존 관계 또한 컨테이너가 대신 처리한다.
DI(Dependency Injection)
의존성
A 객체에서 B 객체의 변수나 메소드를 사용해야 할 경우, A라는 객체 생성자에서 new B();를 해야한다. 이 때 A는 B에 의존한다고 볼 수 있다.
의존성 주입
A라는 객체에서 B를 생성하는 것이 아니라 외부에서 생성된 B를 A에 주입함으로써 의존 관계를 없앤다.
의존성 주입 방법
XML 방법
생성자<constructor-arg> 태그 + ref 속성
속성 <property> + name 속성
Annotation 방법
@Autowired / @Resource
AOP(Aspect of Programming)
트랜잭션이나 로깅, 보안과 같이 여러 모듈에서 공통적으로 사용하는 기능의 경우, 핵심로직에서 분리하여 관리한다.