수많은 개발자들이 정말 간편하게 쓰고 있는 기능이다. 클라이언트에서 영속 계층까지 물 흐르듯 이어지는 로직은 언뜻보면 단순해보이지만 객체지향이 추구하는 정수를 품고있다. 어노테이션 기능은 어떤가, 단지 선언해주는 것만으로 모든 것을 이어주고 연결시켜준다.
신입이었을 때는 '왜 구지 복잡하게 나눠놓고 난리일까.' 라는 생각을 항상 하고는 했다. '스프링을 쓰니까 안 썼을 때랑 비교해서 어땠어요.' 라는 면접관의 질문에, '그냥 더 어렵던데요.' 라는 허접하기 그지없는 대답을 내뱉던 시절도 있었다.
하지만 지금은 절절하게 느낀다. 쓸데없이 느껴지던 MVC 계층도, 그냥 되는 거지 싶던 어노테이션도 셀 수 없이 많은 선배 개발자들의 피눈물과 절망, 숨쉬는 내뱉던 그들의 욕지거리와 쌓여가는 재떨이 속 담배꽁초들, 그럼에도 불구하고 끊임 없이 더 나은 정답을 탐구하던 선구자들이 가져온 봄(Spring)바람이라는 것을.
지금 개발자로 살아가는 우리 역시 끊임 없이 더 나은 것을 만들고, 더 나은 정답을 탐구해야 한다. 동시에 선배들이 제시한 답을 역추적하여 어떠한 애로사항이 있었고, 어째서 이런 답을 제시했는지 역시 탐구해야 한다.
개발자는 이공계지만 우리 생활 속 문제점을 해결한다는 점,
명확한 답은 없고 더 나은 답에 도달한다는 점에서 철학과도 비슷하다.
각설하고, 오늘은 서로 비슷하지만 완전히 다른 @Component와 @Controller 어노테이션. 이 두 가지의 차이점을 알아보고자 한다.
@Component 어노테이션은 클래스에 선언하는 어노테이션이다. 이 어노테이션을 선언해주는 것만으로도 해당 클래스를 스프링 빈(bean) 객체로 사용할 수 있다.
Spring MVC역시 Spring context에서 동작하기 때문에 사용할 모든 클래스를 빈으로 등록해야 할 필요가 있다. Controller 역할을 하는 클래스 역시 마찬가지다. 그런데 문제는 왜 Controller에는 @Component가 아닌 @Controller 어노테이션을 붙이냐는 것이다.
@Component는 해당 클래스를 스프링 빈으로 사용할 수 있게 해준다. @Controller 역시 같은 기능을 가지고 있다. 하지만 @Controller에는 그 이상의 무언가가 있다.
일단 두 어노테이션이 어떤 차이가 있는지부터 알아보자. @Controller가 들어가야 할 자리에 @Component를 넣어보자.
@Component
public class AnnotationController {
@RequestMapping("/test")
public String annotationTest() {
System.out.println("Request reached here");
return "testdir/test";
}
}
자 아주 간단한 예제다. @Controller 대신 @Component를 넣어봤다. 이대로 접속하게 되면 무슨 일이 벌어질까? 과연 잘 작동할까?
시원하게 오류가 떨어졌다. 404에러다. 404에러도 2가지 경우가 존재한다. url 경로는 찾았지만 반환하는 화면을 찾지 못했거나, 혹은 url 경로 자체를 찾지 못했거나.
로그을 아무리 찾아봐도 메서드에 있는 내용이 출력되지 않았다. 그냥 url 경로 자체를 찾지 못한 것이다.
그럼 이제 제대로 @Controller를 붙여서 다시 해보자.
@Controller
public class AnnotationController {
@RequestMapping("/test")
public String annotationTest() {
System.out.println("Request reached here");
return "testdir/test";
}
}
@Component를 @Controller로 바꿨을 뿐이다. 과연 이걸로 해결됬을까?
'잘 왔네'. 잘 된다는 뜻이지.
메서드의 내용 역시 잘 출력된다. @Component와는 달리 @Controller는 url과 클래스를 이어주는 기능을 추가로 가지고 있다.
@Controller는 Controller 클래스에 붙이는 것이다. Controller 클래스는 클라이언트와의 요청, 응답을 주고 받는 역활을 한다.
이 역활을 수행하기 위해서 Controller 클래스는 최소 두 가지의 조건을 만족시켜야 한다.
- HttpServlet의 기능을 수행해야 할 것.
- url(호출)-클래스(서버)-화면(응답)이 서로 연결되어야 할 것.
하지만 위의 소스를 보면 어디에도 저 두 가지 기능을 구현한 부분은 존재하지 않는다. 그렇다면 무엇 덕분에 Controller 클래스는 저 두 가지 기능을 수행 할 수 있는 걸까.
저 두 가지 기능을 가진 어노테이션은 바로...
정답은 @Controller라고? 반은 맞고 반은 틀렸다.
저 두 가지 기능을 수행하는 어노테이션은 @RequestMapping이다.
뭐야, 임마.
물론 @RequestMapping에 대해서 구구절절 설명하고 싶지만, 그 부분은 다음 게시물에서 따로 다루도록 하겠다. 짧지만은 않은 내용이거든.
어쨌든, url과 클래스를 매핑하고 요청과 응답을 이어주는 건 @RequestMapping의 역활이다. 잠깐 간단하게 설명하자면 HandlerMapping이라는 녀석이 url과 클래스를 짝지어주는 역활을 한다.
URL(key) : 스프링 빈(value)
중요한 점은 이거다. url과 스프링 빈이 서로 짝지어진다는 점. 결국 Spring Context에서 이뤄지는 작업들이기 때문에 url로 Controller 클래스를 호출하려면 Controller 클래스가 스프링 빈으로 생성되어야 한다는 점이다.
클래스를 스프링 빈으로 생성하는 방법은 얼추 3가지 정도 있지만, 당장 우리는 @Component를 쓰면 된다는 걸 알고 있다.
그리고 Controller의 기능을 수행하는 어노테이션은 @RequestMapping이다.
이 2가지를 합친다면 @Controller를 대체할 수 있을까?
@Component
@RequestMapping("")
public class AnnotationController {
@RequestMapping("/test")
public String annotationTest() {
System.out.println("Request reached here");
return "testdir/test";
}
}
이걸로 AnnotationController는 스프링 빈 객체 생성되며 동시에 @RequestMapping 기능도 가지게 되었다.
얼추 Spring MVC에서의 Controller 클래스의 구색은 갖춘 것이다. 자 이제 실행 해보자.
응?!이게 되네..
이 부분 첫 줄에 써놨지만 반은 맞고 반은 틀리다고 했다. 그게 대체 무슨 소리인지 한번 알아보자. 그냥 소스 자체를 열어보자. 후욱... 속살을 보자.
일단 떡하니 @Component가 써져있다. @Component의 기능을 포함하고 있는 것이다. 그리고 저 위에 써진 주석. @Controller가 어떤건지에 대해서 설명하고 있다.
그냥 결론적으로 설명하자면
@Controller는 @Component + @RequestMapping 그 이상도 이하도 아닌데...
@Component는 해당 클래스가 스프링 빈으로 생성될 수 있게 해준다.
@RequestMapping은 해당 클래스가 Controller 클래스의 기능을 수행할 수 있게 해준다.
이 부분은 추후에 자세히 다루도록 하겠다.
@Controller는 위의 2가지 어노테이션을 모두 포함하고 있다.
이 또한 선구자들이 찾아낸 단순하지만 직관적인, 아주 의미 있는 해법이다.
가볍게 쓰면 희극이요, 깊게 파고들면 비극이리라.
글을 너무 재밌게 잘 쓰셔서 홀린 듯이 읽었네요! 그런데 최근 스프링 버전과 살짝 맞지 않는 부분이 있어서 너무 좋은 글에 다른 분들이 오해가 있으실까봐서 댓글 남겨놓습니다!
스프링 부트 3.0(스프링 프레임워크 6.0)부터는 클래스 레벨에 @RequestMapping이 있어도 스프링
컨트롤러로 인식하지 않습니다. 오직 @Controller가 있어야 스프링 컨트롤러로 인식합니다. 혹은 @RestController도 인식합니다!
( RequestMappingHandlerMapping에서 @RequestMapping는 인식하지 않고, @Controller만 인식합니다. )
잘읽고갑니다..... 쉽게 잘쓰시네용 감사합니다