글

라벨이 스프링인 게시물 표시

정적리소스 처리

정적 리소스 처리 Servlet 설정 (정적 리소스 처리를 아파치로 하지 않는 경우) 일반적으로 DispatcherServlet을 *.do와 같은 url을 매핑하여 로직을 통한 동적인 리소스를 처리하고, 정적인 리소스는 DefaultServlet으로 처리한다. DefaultServlet에 대한 설정은 톰캣의 web.xml에 설정되어 있으며, *.jsp 요청에 대한 처리를 담당하는 JspServlet도 설정되어있다. // DefaultServlet 설정 <servlet> <servlet-name>default</servlet-name> <servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class> <init-param> <param-name>debug</param-name> <param-value>0</param-value> </init-param> <init-param> <param-name>listings</param-name> <param-value>false</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>default</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> ​ // JspServlet 설정...

ExceptionHandler_언제_어떻게

@ExceptionHandler: 언제 어떻게 동작하는가. @RequestParam이나 @Pathvariable, @RequestBody 등 요청 파라미터를 매핑하는 과정에서 에러가 발생했을 시, 즉 Controller에서 @RequestMapping 메소드가 실행되기전에 에러가 발생해도도 ExceptionHandler가 동작할까? ex) @RequestParam int a 일 때, a에 String 값을 넘기는 경우. 결론은 동작한다. ExceptionHandler가 동작하는 부분을 찾아보기 위해 DispatcherServlet의 doDispatch() 메소드를 살펴봤다. 아래 메소드를 보면 try ~ catch로 묶인 부분을 보면, request에 대해 handler를 가져와 실행되는 모든 과정에서 exception이 발생하는 경우 catch절에서 잡히게된다. 에러가 발생하여 catch절 온다면 위임할 exception을 담아둔다. 마지막으로 processDispatchResult에서 핸들러 메소드 실행 결과를 처리한다. protected void doDispatch(HttpServletRequest req, HttpServletResponse res) { try { processedRequest = checkMultipart(req); multipartRequestParsed = (processedRequest != req); // 매핑가능한 handler 가져오기 mappedHandler = getHandler(processedRequest); ... ​ HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler()); ... ​ // p...

slf4j - logback 설정 정리

slf4j - logback 설정 정리 @ 스프링은 자체적으로 JCL을 사용 -> slf4j + logback으로 셋팅 spring-core는 내부적으로 JCL을 사용하는데, 문제는 JCL이 실제 로거를 선택하는 시점이 런타임 시점이라서 대상 클래스를 불러오거나 오버헤드가 발생할 수 있다고 한다. 그런데 slf4j는 이런 문제들을 해결해준다. ​ 1. slf4j + logback 디펜던시를 추가한다. logback-classic 안에는 logback-core와 slf4j-api가 포함되어있다. <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.1.11</version> </dependency> 2. slf4j로 전환하기 위해서 spring과 jcl의 의존성을 제거한다. spring-core에서 jcl 디펜던시를 제거 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.14.RELEASE</version> <exclusions> <exclusion> <groupId>commons-logging</groupId> <artifactId>commons-logging</artifactId> </exclusion> </exclusions> </dependency> spring 내부...

로깅프레임워크

이미지
자바 로깅 프레임워크 (log4j, logback, slf4j) @ Log4j log4j는 로깅을 수행하는 로깅 구현체다. 1. 주요 기능 및 특징 속도에 최적화되었다. 멀티 스레드 환경에 안전하다. 계층적 로그 설정 및 처리할 수 있다. 6가지의 계층적 로그 레벨 파일, 콘솔, 원격 서버, 이메일 등 다양한 형태로 로그를 기록할 수 있다. 단, 로그에 대한 파일 입출력시 오버헤드가 발생 할 수 있다. 개발 중간에 로그 설정을 변경하면, 서버를 재시작해야한다. 2. 주요 구조 Appender : 파일, 콘솔, 원격 서버 등 로그를 기록할 위치를 지정한다. ConsoleAppender : 콘솔에 로그를 남긴다. FileAppender : 파일에 로그를 남긴다. RollingFileAppender : 로그의 크기가 일정 용량이 되면 다른 파일에 남긴다. DailyRollingFileAppender : 하루 단위로 파일에 로그를 남긴다. SMTPAppender : 이메일에 로그를 남긴다. Layout : 기록할 로그의 패턴을 지정한다. Logger : 로깅일 일어나는 부분을 그룹화하여, 필요한 그룹의 로그만 출력하도록 지정한다. 3. 로그 레벨 FATAL < ERROR < WARN < INFO < DEBURG < TRACE 순으로 6가지의 레벨이 있다. 좌측으로 갈수록 좁은 범위의 크리티컬한 로그 레벨을 의미하고, 우측으로 갈수록 넓은 범위를 로그 레벨을 의미한다. DEBURG로 설정하는 경우, FATAL / ERROR / WARN / INFO / DEBURG 레벨의 로그들을 기록한다. @Logback log4j와 마찬가지로 로깅을 수행하는 로깅 구현체다. logback은 log4j 개발자가 기존의 log4j를 대체하기 위해 개발한 로깅 프레임워크다. log4j를 토대로 만들어서 더 높은 성능과 안정성을 보장할 수 있다...

@PutMapping / @DeleteMapping : @RequestParam 사용하기

이미지
PutMapping / DeleteMapping : Body 읽기 @PutMapping이나 @DeleteMapping을 통해 리소스 id를 @PathVariable로 받고 그 외의 변수들은 @RequestParam으로 받으려고 했을 때, query string이 아닌 body에 데이터를 넣고 전송하려하면 정상적으로 작동하지 않는다. body에 담긴 데이터가 POST Method에서는 @RequestParam에는 매핑이 되는데 왜 PUT, DELETE는 안되나 싶어서 찾아봤다. 해결 방법 1 이슈되는 부분은 spring이 아닌 tomcat에 있었다. tomcat에서 request의 body 내용을 HTTP POST Method 일 때만 파싱해오도록 설정되어 있기 때문이다. 참고 [1] 이를 해결하기 위한 한가지 방법으로 HTTP DELETE / PUT Method 일 경우에도 request의 body 내용을 파싱하도록 설정하는 것이다. 그 설정은 tomcat의 server.xml에서 parseBodyMethods=“POST, DELETE” 이런 식으로 설정해 주는 것이다. 참고 [2] ​ 이 설정이 어떻게 실행되는지 톰캣 라이브러리를 보면 다음과 같다. 참고 [3] Connector.java 아래 그림을 보면 parseBodyMethods의 디폴트 값이 POST이라서, HTTP POST Method일 때는 별다른 설정없이 body 내용을 잘 가져올 수 있다. parseBodyMethods의 getter, setter를 미루어보아, server.xml에서 설정한 문자열을 그대로 저장할 뿐만아니라 여러 개일 경우 ','로 잘라서 hashset 형태로 저장해놓는 것을 볼 수 있다. Request.java 이번에는 실제로 request가 들어오면 parseBodyMethods로 설정한 HTTP Method에 있는지 비교하는 부분이다. 해결 방법 2 또 다른 방...

DIspatcher-doDisPatch

DispatcherServlet - doDispatch 1. checkMultipart multipartResolver가 있고 request가 MultiPart이면, multipartResolver로 AbstractMultipartHttpServletRequest를 상속받는 MultiPartRequest로 변환해서 반환 request에서 File을 꺼내서 그게 아니면 그냥 그대로 request 반환 2. getHandler getHandlerInternal : 해당 request를 통해 lookupPath를 찾아서 맞는 handlerMethod 검색 getHandlerExecutionChain : 해당 request를 통해 lookupPath를 찾아서 매치되는 interceptor들을 추가한 HandlerExecutionChain 반환 3. getHanlerAdapter getHandler로 얻은 handler를 위한 HandlerAdapter 를 찾아서 반환 4. HandlerExecutionChain.applyPreHandle interceptor들 실행 5. HandlerAdapter.handle invokeHandlerMethod를 통해 실제 handlerMethod를 실행시키고 ModelAndView를 반환 ServletInvocableHandlerMethod에다가 ArgumentResolvers, ReturnValueHandlers, DataBinderFactory, ParameterNameDiscoverer를 셋업하고 ModelAndViewContainer에 넣음 ServletInvocableHandlerMethod.invokeAndHandle : 실제 실행 getModelAndView : viewName을 통해 ModelAndView를 얻어서 반환 6. HandlerExecutionChain.applyPostHandle interceptor들...

어노테이션 기반 기본 설정

이미지
어노테이션 기반 설정 <context:component-scan base-package="…"/> xml 파일에 직접 빈 등록 설정을 하지 않고, 자동으로 설정되게하는 엘리먼트이다. 이 설정을 통해 스프링 컨테이너는 지정한 클래스 패스에 있는 클래스들 중 @Component 어노테이션이 붙은 클래스들을 스캔하고 빈으로 등록한다. 빈 등록을 위한 어노테이션 @Component("…") 빈으로 등록하기위해 클래스 선언부 위에 설정하는 어노테이션이다. 당연히 클래스의 기본 생성자가 있어야만 컨테이너가 빈으로 등록할 수 있다. 또한, 컨테이너가 생성한 객체를 사용하기 위해서는 id나 name과 같은 이름을 설정해주어야 한다. 설정하지 않은 경우에는 자동으로 클래스명에서 앞글자를 소문자로 바꾼 이름으로 등록된다. @Controller | @Service | @Repository 어노테이션들은 @Component를 상속한 것으로, 각각 MVC 역할을 수행하는 클래스임을 분류하고 인식되도록 하는 어노테이션들이다. 의존성 주입을 위한 어노테이션 @Autowired 스프링에서 제공하는 어노테이션이다. 객체의 타입을 이용하여, 등록된 빈들 중 일치하는 것을 찾아 주입하는 어노테이션이다. 주입하기위해서는 빈으로 등록된 객체이어야 한다. 등록된 빈이 없거나 같은 타입으로 등록된 빈이 2개 이상이면, NoSuchBeanDefinitionException이 발생한다. 이를 방지하기 위해 required 속성을 false로 주거나 @Qualifier 어노테이션을 사용하면 된다. 여기서 같은 타입의 빈이 여러개일 때 @Qualifier가 없이도 주입을 할 수 있다. 빈 이름을 변수명으로 사용하는 경우이다. 다시 한번 정리해보면, 다음과 같다. 객체 타입의 빈이 없다 -> ERROR 객체 타입의 빈이 유일하다 -> OK 객체 타입의 빈이 여...

스프링 XML 기본 설정

스프링 XML 설정 루트 엘리먼트 스프링에 빈을 등록하기 위해 을 루트 엘리먼트로 사용하고 그안에 자식 엘리먼트들을 이용해야한다. 자식 엘리먼트들로는 , , , 등이 있다. 엘리먼트 스프링 설정에는 단순한 빈설정 이외에 트랜잭션 관리, 예외 처리, 다국어 관리 등이 있다. 하나의 XML 설정파일에 모든 걸 설정하기에는 유지보수에 적합하지 않다. 따라서 기능별이나 주제별 등으로 분리하여 관리하여 보관하는 것이 바람직하다. 엘리먼트는 이렇게 분리된 설정 파일들을 불러와 통합할 때 사용된다. 루트 설정 파일에서 엘리먼트를 사용하면, 마치 여러 설정 파일을 하나의 파일로 포함한 것으로 처리되는 느낌이다. 엘리먼트 스프링 설정 파일에서 클래스를 빈으로 등록할 때 사용한다. id와 class 속성을 이용할 수 있으며, class 속성은 필수값이다. id 속성을 지정하지 않아도 빈으로 등록되는 것은 문제가 없지만, 빈에 대한 유일한 식별자가 없게된다. id 속성이외에 name 속성이 있으며, 둘의 차이는 id 속성 값으로는 자바 식별자와 같은 규칙을 가지지만, name 속성에는 특별한 규칙이 없다는 것이다. 하지만 둘다 유일한 값이어야 한다는 것은 일치하며, 왠만해서는 규칙에 따른 id값을 사용하는 것이 좋겠다. 속성 정리 init-method 스프링 컨테이너는 빈을 등록하기 위해 객체를 생성할 때, 디폴트 생성자만 인식한다. 따라서, 초기화 작업 등의 별도의 실행할 메소드가 필요하다면 지정할 수 있다. destroy-method init-method와 마찬가지로, 스프링 컨테이너가 빈을 삭제할 때 호출하는 메소드를 지정할 수 있다. lazy-init ApplicationContext 컨테이너를 이용하는 경우에는 구동하는 시점에서 즉시 빈등록이 된다고 했는데, 해당 속성을 통해 lazy-loading을 지정할 수 있다. sco...

AOP 기본 개념

AOP - Aspect Oriented Programming 스프링의 IoC가 결합도와 관련된 기능이라면 AOP는 응집도와 관련된 기능이다. 여러 비지니스 로직에서 공통적으로 수행되는 부분을 분리하여 효율적으로 관리하는데 핵심이다. 결론적으로 공통 코드를 제거하고, 그 공통 코드를 효율적으로 관리하고자 하는 기법이다. 보통 이런 공통 코드를 횡단 관심사라는 용어로 부르며, 실제로 주목해야하는 비지니스 로직을 핵심 관심사라고 표현한다. AOP 용어 횡단 관심사라는 용어처럼 AOP에는 다양한 용어들이 있다. 1. 조인포인트 - Joinpoint 조인포인트란 클라이언트가 호출하는 모든 비지니스 메소드 를 말한다. 2. 포인트컷 - Pointcut 앞서 말했듯이 조인포인트가 모든 비지니스 메소드라면, 포인트컷은 필터링된 조인포인트 를 말한다. 예를들어, 트랜잭션 기능은 CRUD 중에 CUD에만 필요하므로 R 메소드를 필터링한 것을 포인트컷이라 한다. 3. 어드바이스 - Advice 횡단 관심 에 해당하는 공통 코드를 말한다. 어드바이스가 언제 실행될지에 대해 설정은 before, after, after-returning … 등등으로 설정할 수 있다. 4. 위빙 - Weaving 위빙은 포인트 컷으로 지정한 핵심 관심 메소드가 호출될 때, 어드바이스에 해당하는 횡단 관심 메소드가 삽입되는 과정 을 말한다. 위빙을 처리하는 방식으로 크게 컴파일타임 위빙, 로딩타임 위빙, 런타임 위빙이 있지만, 스프링에서는 런타임 위빙 만 지원한다. 5. 애스팩트 - Aspect / 어드바이저 - Advisor 애스팩트란 포인트컷과 어드바이스의 결합으로, 어떤 포인트컷 메소드에 대해 어떤 어드바이스 메소드를 실행할지 결정한다. 애스팩트와 어드바이저는 같은 의미의 용어이지만, 설정하는 엘리먼트는 <aop:advisor …/>가 따로 있다. 대표적으로 어드바이저를 사용하는 경우가 트랜잭션 설정이다...

스프링의 의존성 관리 방법

스프링의 의존성 관리 방법 스프링 프레임워크의 가장 중요한 특징은 객체의 생성과 의존관계를 컨테이너가 자동으로 관리한다는 점이며, 이것이 스프링 IoC의 핵심원리이다. 스프링은 두 가지 형태로 IoC를 지원한다. Dependency Lookup Dependency Injection 스프링 의존성 관리 방식 1. Dependency Lookup 컨테이너가 객체들을 생성하여 로딩하면, 클라이언트는 컨테이너가 생성한 객체를 검색하여 사용하는 방식을 말한다. 톰캣의 서블릿 컨테이너를 이용하여 서블릿을 사용하는 것과 같은 방식을 말한다. 2. Dependency Injection 객체간의 의존관계를 스프링 설정 파일에 등록된 정보를 바탕으로 컨테이너가 자동으로 처리해주는 방식을 말한다. 이 방식에는 Setter 메소드를 기반으로하는 세터 인젝션과 생성자를 기반으로 하는 생성자 인젝션으로 나뉜다. 생성자 인젝션 스프링 컨테이너는 XML 파일을 이용해 빈을 생성할 때, 기본적으로 디폴트 생성자를 이용한다고 했다. 하지만, 디폴트 생성자말고 매개변수가 있는 생성자를 호출할 수 있도록 설정할 수 있으며, 이 기능을 이용해 생성자 인젝션을 처리한다. 매개변수로 의존관계에 있는 객체의 주소정보를 전달 아래와 같이 설정할 수 있으며, 알아두어야 할점은 빈의 등록 순서이다. 기본적으로 XML 설정파일에 등록된 순서대로 디폴트 생성자를 이용해 객체를 생성하지만, 생성자 인젝션을 사용하는 경우에는 의존 관계가 되는 객체를 먼저 생성하고 매개변수가 있는 생성자를 통해 최종적으로 빈등록을 마친다. <bean id="samsungTv" class="TV"> <constructor-arg ref="samsungSpeaker"></constructor-arg> </bean> <bean id="samsungSpeake...

스프링 주요 기술

스프링 삼각형과 설정정보 스프링을 이해하기 위해서는 POJO를 기반으로한 스프링 삼각형이라 불리는 3대 프로그래밍 모델에 대한 이해가 필수적입니다. 스프링 삼각형을 구성하는 3대 프로그래밍 기술이란 Ioc/DI, AOP, PSA가 있습니다. 1. IoC/DI 의존성이란 스프링의 IoC라고 부르는 DI를 알아보기 전에, 먼저 프로그래밍에서 의존성이랑 개념은 다음과 같습니다. 자동차는 내부적으로 타이어를 가진다. = Car 객체는 new Tire()를 가진다. = Car가 Tire에 의존한다. 즉, 프로그램에서 의존성이란 전체가 부분에 의존하며 new 를 통해 관계가 표현 되는 것을 말합니다. 조금 더 깊이 들어가서 의존하는 객체(전체)와 의존되는 객체(부분) 사이에 집합 관계나 구성 관계로 구분지을 수 도 있는데, 아무튼 전체가 부분에 의존한다는 개념입니다. 집합 관계 : 부분이 전체와 다른 생명 주기를 가질 수 있습니다. (ex) 집 - 냉장고) 구성 관계 : 부분이 전체와 같은 생명 주기를 가질 수 있다. (ex) 사람 - 심장) 다음 예시는 자동차와 타이어에 대한 의존성을 나타낸 코드입니다. 자동차 클래스 생성자 내부에서 타이어에 대한 의존관계가 일어났습니다. interface Tire() { String getBrand(); } ​ public class KoreaTire implements Tire { public String getBrand() { return "코리아 타이어"; } } ​ public class AmericaTire() implements Tire { public String getBrand() { return "아메리카 타이어"; } } public class Car { Tire tire; ...