728x90
반응형

컨트롤러를 지정하기 위한 어노테이션은 @Controller @RestController가 있다. 두 어노테이션의 차이 점은 HTTP Response Body가 생성되는 방식이다.

 

● @Controller

-  Controller로 View 반환하기

 전통적인 Sprinv MVC의 컨트롤러인 @Controller는 주로 View를 반환하기 위해 사용한다. 아래와 같은 과정을 통해 Spring MVC Container는 Client의 요청으로부터 View를 반환한다.

Controller View Data 반환

  • Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
  • DispatcherServlet이 요청을 위임할 HandlerMapping을 찾는다.
  • HandlerMapping을 통해 요청을 Controller로 위임한다.
  • Controller는 요청 처리후 ViewName을 반환한다.
  • DispatcherServlet은 ViewResolver를 통해 ViewName에 해당하는 View를 찾아 사용자에게 반환한다.

Controller가 반환한 뷰의 이름으로부터 View를 렌터딜하기 위해서는 ViewResolver가 사용되며, ViewResolver 설정에 맞게 View를 찾아 렌더링 한다.

Controller로 Data 반환하기

바로 Data를 반환해야 하는 경우도 있다. 컨트롤러에서는 데이터를 반환하기 위해 @ResponseBody 어노테이션을 활용해 주어야 한다. 이를 통해 Controller도 JSON 형태로 데이터를 반환 할 수 있다.

Controller JSON Data 반환

 

  • Client는 URI 형식으로 웹 서비스에 요청을 보낸다.
  • DispatcherServlet이 요청을 위임할 HandlerMapping을 찾는다.
  • HandlerMapping을 통해 요청을 Controller로 위임한다.
  • Controller는 요청을 처리한 후에 객체를 반환한다.
  • 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환된다.

컨트롤러를 통해 객체를 반환할 경우 일반적으로 ResponseEntity로 감싸서 반환한다. 그리고 객체를 반환하기 위해선 viewResolver 대신 HttpMessageConverter가 동작한다. HttpMessageConverter에는 여러 Converter가 등록되어 있고, 반환해야 하는 데이터에 따라 사용되는 Converter가 달라진다.

스프링은 클라이언트의 HTTP Accept 헤더와 서버의 컨트롤러 반환 타입 정보 둘을 조합해 적합한 HttpMessageConverter를 선택하여 처리한다. MessageConverter가 동작하는 시점은 HandlerAdapter와 Controller가 요청을 주고 받는 시점이다. 

@Controller
    @RequiredArgsConstructor
    public class UserController {

        private final UserService userService;

        @GetMapping(value = "/users")
        public @ResponseBody ResponseEntity<User> findUser(@RequestParam("userName") String userName) {
            return ResponseEntity.ok(userService.findUser(user));
        }

        @GetMapping(value = "/users/detailView")
        public String detailView(Model model, @RequestParam("userName") String userName) {
            User user = userService.findUser(userName);
            model.addAttribute("user", user);
            return "/users/detailView";
        }
    }

 findUser는 User 객체를 ResponseEntity로 감싸서 반환하고 있고, User를 JSON으로 반환하기 위해 @ResponseBody라는 어노테이션을 붙여주고 있다. detailView 함수에서는 View를 전달해주고 있기 때문에 String을 반환값으로 설정해주었다.

 

● @RestController

 - @Controller에 @ResponseBody가 추가된 것이다. 주 용도는 JSON 형태로 객체 데이터를 반환하는 것이다. 최근엔 데이터를 응답으로 제공하는 REST API를 개발 할 때 사용되며 객체를 ResponseEntity로 감싸서 반환한다.

RestController 동작 방식

 

  • 클라이언트는 URI 형식으로 웹 서비스에 요청을 보낸다.
  • DispatcherServlet이 요청을 위임할 HandlerMapping을 찾는다.
  • HandlerMapping을 통해 요청을 Controller로 위임한다.
  • Controller는 요청을 처리한 후에 객체를 반환한다.
  • 반환되는 객체는 Json으로 Serialize되어 사용자에게 반환된다.
@RestController
    @RequestMapping("/user")
    @RequiredArgsConstructor
    public class UserController {

        private final UserService userService;

        @GetMapping(value = "/users")
        public User findUser(@RequestParam("userName") String userName){
            return userService.findUser(user);
        }

        @GetMapping(value = "/users")
        public ResponseEntity<User> findUserWithResponseEntity(@RequestParam("userName") String userName){
            return ResponseEntity.ok(userService.findUser(user));
        }
    }

 findUser는 User 객체를 그대로 반환하고 있다. 이 경우 문제는 클라이언트가 예상하는 HttpStatus를 설정해 줄수 없다는 것이다. 예를 들어 어떤 객체의 생성 요청이라면 201 CREATED를 기대 했지만 객체를 그대로 반환하면 HttpStatus를 설정해줄 수 없다. 그래서 객체를 상황에 맞는 ResponseEntity로 감싸서 반환해 주어야 한다.

https://mangkyu.tistory.com/49?category=761302 

 

[Spring] @Controller와 @RestController 차이

Spring에서 컨트롤러를 지정해주기 위한 어노테이션은 @Controller와 @RestController가 있습니다. 전통적인 Spring MVC의 컨트롤러인 @Controller와 Restuful 웹서비스의 컨트롤러인 @RestController의 주요한 차..

mangkyu.tistory.com

 

728x90
반응형
728x90
반응형

클라이언트에서 받은 요청을 객체로 바인딩 하기 위해 사용하는 방법에 알아보자

@RequestParam

- 1개의 HTTP 요청 파라미터를 받기 위해 사용한다. 필수 여부가 true로 설정되어 있기에 반드시 해당 파라미터가 반드시 해당 파라미터가 전송되어야 하며, 파라미터가 전송되니 않으면 400 에러가 발생한다. 반드시 필요한 값이 아니라면 required를 false로 설정해 주면 된다.(defaultValue 옵션을 사용하면 기본값을 지정할 수 있다.)

 

 @RequestBody

- 클라이언트가 전송하는 JSON(application/json) 형태의 HTTP Body를 Java 객체로 변환 시켜주는 역할을 한다. 그렇게 때문에 Body가 존재하지 않는 HTTP Get 메소드에 @RequestBody를 활용하려고 하면 에러가 발생한다. @RequestBody로 받는 데이터는 스프링에서 관리하는 MessageConverter들 중 하나인 MappingJackson2HttpMessageConverter를 통해  Java 객체로 변환되는데, 이는 ObjectMapper 라는 클래스를 사용한다. 물론 데이터 형식이 Json이 아닐 수도 있다.

ObjectMapper 동작 과정

더보기

ObjectMapper는 JSON 메시지를 자바 객체로 변환하는 과정에서 객체의 기본 생성자를 통해 객체를 생성하고, 내부적으로 Reflection을 사용한다. 그래서 반드시 Setter가 필요한 것은 아니다. Getter나 Setter 혹은 JsonInclude 등 필드에 있는 변수들의 이름을 찾기 위한 메소드들이 필요하다. 그러므로 기본생성자 + Getter로 클래스를 구현 하면 Setter가 없어도 값이 바인딩된다.

 

@ModelAttribute

- 클라이언트가 전송하는 폼(form)형태의 HTTP Body와 요청 파라미터들은 생성자나 Setter로 바인딩 하기 위해 사용된다. 매핑 시키른 파라미터의 타입이 객체으 타입과 일치하는지 등을 포함한 다양한 검증(Validation) 작업이 추가적으로 진행 되는 데, Long형 index에 "1번"을 String형을 넣을려고 한다면, BindException이 발생하게 된다.

특정 Parameter 값 만 가져올 수도 있다. 만약 다음과 같은 형태로 전송된 데이터에서 @ModelAttribute("writer") String writer을 사용하면 writer 변수에서 얻어진다.

ModelAttribute 동작 과정

더보기

Reflection을 사용해 필드를 인자로 받는 생성자가 있는지 검사한다. 만약 있다면 해당 생성자를 이용해 값을 세팅하고 없다면 Setter로 값을 세팅한다. 예를 들어 모든 필드를 인자로 받는 생성자가 있다면 해당 생성자로 값을 주입한다. 만약 없다면 일부 필드를 인자로 받는 생성자를 찾아 사용하고, 없으면 기본 생성자를 사용해 객체를 생성한다. 그리고 생성자로 주입하지 못한 남은 변수들은 Setter를 이용해 값을 할당한다. 예를 들어 다음의 경우에도 요청만 정상적으로 왔다면 모든 값이 바인딩되는 것이다. 아래의 경우라면 먼저 Board(1, "글쓴이")를 이용해 Board 객체가 생성되고, 그 다음에 Setter를 통해 contents에 값이 주입된다.

@Getter
@Setter
@ToString
public class Board {

    private int index;
    private String writer;
    private String contents;

    public Board(int index) {
        this.index = index;
    }

    public Board(int index, String writer) {
        this.index = index;
        this.writer = writer;
    }
}

RequestBody, ModelAttribute, RequestParam 활용 예시

 - Model 객체

@Getter
@Setter
@ToString
public class Board {

    private int index;
    private String writer;
    private String contents;

}

  - Spring에서 활용 예시

@RestController
    @RequestMapping("/board")
    public class BoardController {

        @PostMapping("/requestBody")
        public ResponseEntity<Board> requestBody(@RequestBody Board board) {
            // @RequestBody는 MessageConverter를 통해 Json 형태의 HTTP Body를 Java 객체로 변환시킨다.
            return ResponseEntity.ok(board);
        }

        @PostMapping("/modelAttribute")
        public ResponseEntity<Board> modelAttribute(@ModelAttribute Board board) {
            // @ModelAttribute는 폼(form) 형태의 HTTP Body와 요청 파라미터들을 객체에 바인딩시킨다.
            return ResponseEntity.ok(board);
        }

        @GetMapping("/list")
        public ResponseEntity<List<Board>> requestParam(
                @RequestParam(value = "searchKeyWord1", required = false, defaultValue = "MangKyu") String searchKeyWord) {

            // searchKeyWord는 required가 false이기 때문에 없을 수도 있으므로, 없다면 기본값이 할당된다.
            return ResponseEntity.ok(boardService.getBoardList(searchKeyWord1));
        }
    }

ModelAttribute는 multipart/form-data 형태의 HTTP Body와 파라미터들을 생성자나 Setter로 바인딩 시킨다고 했다. 그렇기에 만약 요청이 다음과 같은 경우라도 객체의 모든 데이터는 정상적으로 바인딩된다.

Request URL: http://localhost:8080/board/modelAttribute?contents=Contents

Request Body:
  - type: application/x-www-form-urlencoded
  - data: {
      "index": 1,
      "writer": "toyeka"
    }
    
    
Response Body:
  - data: {
      "index": 1,
      "writer": "toyeka",
      "contents: "Contents"
    }

 

간단 정리

 - RequestParam

  • 1개의 HTTP 파라미터를 얻기 위해 사용되며 기본값을 지정 할수 있다.
  • 필수 여부가 true이기 때문에 반드시 필요한 경우가 아니라면 required=false 설정이 필요하다.

 - RequestBody

  • JSON(application/json) 형태의 HTTP Body 데이터를 MessageConverter를 통해 Java 객체로 변환시킨다.
  • 기본 생성자로 객체를 만들고, Getter나 Setter 등의 메소드로 필드를 찾아 Reflection으로  값을 설정한다.

 - ModelAttribute

  • 폼 형태(form)의 HTTP Body와 요청 파라미터들을 객체에 바인딩한다.
  • 기본적으로 생성자로 값이 설정되고, 생성자로 설정되지 않는 필드는 Setter로 설정된다.

https://mangkyu.tistory.com/72?category=761302 

 

[Spring] @RequestBody, @ModelAttribute, @RequestParam의 차이

이번에는 Spring에서 Client로 받은 요청을 객체로 바인딩하기 위해 사용하는 방법들에 대해서 알아보도록 하겠습니다. 1. RequestBody, ModelAttribute, RequestParam이란? [ @RequestParam ] @RequestParam은..

mangkyu.tistory.com

 

728x90
반응형
728x90
반응형

📖 본문

1. URL 입력 후 서버로 요청이 가는 과정

브라우저 주소창에 URL을 입력하면, 사실 단순히 “주소”만 보낸 게 아니라 서버에 HTTP Method라는 방식으로 요청을 보낸 것입니다.
HTTP Method는 여러 종류가 있지만, 가장 기본이자 많이 쓰이는 건 GETPOST입니다.


2. GET 방식 — "책 빌려오는 방법"

📌 개념
필요한 정보를 가져오기 위한 요청 방식입니다.
마치 도서관에서 책을 빌려오는 것과 비슷하죠.

📝 특징

  • URL에 데이터가 포함됩니다.
https://example.com/login?id=abcd&pw=kor
  • → ? 뒤의 id와 pw가 서버로 전달됩니다.
  • 데이터가 Header에 포함되어 전송됩니다.
  • URL에 데이터가 노출 → 보안에 취약
  • 캐싱 가능 (속도 향상, 즐겨찾기 가능)
  • 데이터 전송 길이 제한 존재 (브라우저 제한)
  • 바디(Body)는 보통 비어 있음

⚠️ 주의
아이디나 비밀번호 같이 민감한 정보는 절대 GET으로 보내면 안 됩니다.


3. POST 방식 — "편지 봉투 전달하기"

📌 개념
데이터를 서버로 제출하여 추가, 수정하는 방식입니다.

📝 특징

  • 데이터는 Body에 포함되어 전송 → URL에 안 보임
  • 기본적으로 보안이 GET보다 나음 (하지만 암호화 필요)
  • 캐싱 불가
  • 길이 제한 없음 (하지만 Time Out 존재)
  • Content-Type 헤더로 데이터 형식 명시 필요
  • 쿼리스트링 뿐 아니라 폼 데이터, 파일 전송 가능

💡 예시
로그인 폼, 회원가입, 게시글 작성 같은 데이터 입력 기능


4. 캐싱(Caching)이란?

한 번 불러온 데이터를 임시 저장해 두었다가, 다시 요청할 때 더 빠르게 가져오는 기술입니다.
예: 이미지, CSS, JS 파일을 캐싱하면 페이지 로딩 속도가 빨라집니다.


📌 정리 표

구분                                                                        GET                                                             POST
데이터 위치 URL Body
보안성 낮음 높음(기본)
캐싱 가능 불가능
전송 길이 제한 있음 제한 없음
사용 예시 검색, 조회 로그인, 등록, 수정
 

 

728x90
반응형
728x90
반응형

목차

  1. 서블릿이란? (쉬운 비유)
  2. 서블릿의 핵심 특징
  3. 서블릿 컨테이너(예: Tomcat)의 역할
  4. 서블릿 동작 흐름(요청→매핑→doGet/doPost→응답)
  5. 서블릿 생명주기(init → service → destroy)
  6. JSP는 뭐고, 서블릿과 어떻게 다른가
  7. 실전 예제: HelloServlet (Annotation 기반)
  8. 자주 하는 실수 & 베스트 프랙티스
  9. 마무리 체크리스트(면접/시험 대비)

1) 서블릿이란? (쉬운 비유)

사용자(브라우저)가 “로그인해줘!”라고 부탁하면, 서블릿이 그 요청을 받아 DB를 확인하고 결과 페이지(응답)를 만들어 돌려주는 자바 클래스입니다.

  • “요청을 받아 응답을 만든다” = Controller 역할에 딱 맞음.

2) 서블릿의 핵심 특징

  • 동적 웹 컴포넌트: 요청 시점에 필요한 내용을 만들어 응답.
  • HttpServlet 상속: javax.servlet.http.HttpServlet(Jakarta EE에선 jakarta.servlet.http.HttpServlet)을 상속.
  • 스레드 기반 동작: 컨테이너가 요청마다 스레드를 재사용/관리.
  • MVC에서 Controller로 자주 사용.
  • (구)HTML을 자바 코드로 println 하던 시대 → 유지보수 어려워서 JSP/템플릿과 분리해서 씀.

3) 서블릿 컨테이너의 역할 (Tomcat 대표)

  • 웹 서버와 통신을 대신 처리(소켓, HTTP 파싱 등).
  • 생명주기 관리: 로딩→init()→요청 시 service()→종료 시 destroy().
  • 멀티스레드 처리: 요청마다 스레드 할당/관리.
  • 선언적 보안: 설정 파일/어노테이션 기반으로 인증·인가 처리.

4) 서블릿 동작 흐름

  1. 클라이언트가 URL 요청 → 컨테이너가 HttpServletRequest/Response 생성
  2. URL 매핑(web.xml 또는 @WebServlet)으로 대상 서블릿 찾기
  3. service()가 호출되어 메서드 분기(doGet/doPost 등)
  4. 서블릿이 응답을 만들어 HttpServletResponse로 전송
  5. 컨테이너가 마무리 정리

5) 서블릿 생명주기 (Life Cycle)

  • init(): 서블릿 최초 로딩 시 1회 호출(공유 리소스 초기화에 적합)
  • service(): 매 요청마다 호출되어 doGet()/doPost()로 분기
  • destroy(): 컨테이너 종료/교체 시 1회 호출(정리 작업)
[로딩] → init() → (요청 n번) service() → ... → destroy() → [언로드]

6) JSP는 무엇이고, 서블릿과 어떻게 다른가

  • JSP(Java Server Pages): HTML 안에 자바를 삽입해 화면 구성에 특화.
  • 최종적으로 JSP도 서블릿으로 변환되어 실행됨.
  • 일반적으로 서블릿=로직/제어, JSP=뷰 로 분리해서 MVC 구조를 만든다.

7) 실전 예제: HelloServlet (Annotation 기반)

// jakarta.servlet-api를 사용하는 최신 톰캣 기준 예시
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;

import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {

        req.setCharacterEncoding("UTF-8");
        resp.setContentType("text/html; charset=UTF-8");

        PrintWriter out = resp.getWriter();
        out.println("<!DOCTYPE html>");
        out.println("<html><head><meta charset='UTF-8'><title>Hello</title></head>");
        out.println("<body><h1>안녕하세요, Servlet!</h1>");
        out.println("<p>요청 URI: " + req.getRequestURI() + "</p>");
        out.println("</body></html>");
    }
}

Tip: 구형 프로젝트는 web.xml 매핑을 쓰지만, 요즘은 @WebServlet("/path") 어노테이션 매핑이 간편합니다.

8) 자주 하는 실수 & 베스트 프랙티스

  • 인스턴스 변수에 상태 저장 금지: 서블릿은 다중 스레드가 공유합니다.
  • 문자 인코딩 설정: resp.setContentType("text/html; charset=UTF-8") 등 필수.
  • 비즈니스 로직 분리: DAO/Service로 분리하고 서블릿은 흐름 제어만.
  • 에러/예외 처리: 공통 에러 페이지, 필터/인터셉터로 로깅과 인증 처리.
  • JSP에서 스크립틀릿 최소화: EL/JSTL, 템플릿 엔진(Thymeleaf 등)로 표현만 담당.

9) 마무리 체크리스트 (면접/시험 포인트)

  • 서블릿과 JSP의 관계 설명 가능?
  • 컨테이너 역할(생명주기·스레드·보안) 술술 말할 수 있는지?
  • Life Cycle 순서/용도 정확히 아는지?
  • URL 매핑(@WebServlet vs web.xml) 차이 이해했는지?

SEO용 FAQ

Q1. 서블릿은 왜 필요한가요?
A. HTTP 요청을 자바로 처리해 동적 페이지를 만드는 표준 컨트롤러이기 때문입니다.

Q2. JSP랑 뭐가 달라요?
A. JSP는 화면 템플릿, 서블릿은 로직/흐름 제어. JSP도 결국 서블릿으로 변환돼 실행됩니다.

Q3. 스프링 MVC랑 관계는?
A. 스프링 MVC도 내부적으로 서블릿(DispatcherServlet)을 사용합니다. 서블릿 개념을 알면 스프링이 쉬워집니다.

728x90
반응형
728x90
반응형

● IoC(Inversion of Control)란?

- IoC는 제어의 역전이라는 뜻으로 프로그램의 제어의 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 말한다. 이전에는 개발자가 객체를 생성하고 관리하며 프로그램의 제어 흐름을 스스로 조종했다. 하지만 Spring을 이용하면 스프링 컨테이너가 프로그램의 제어흐름을 제어하게 된다.

스프링 컨테이너(Spring Container)란?

 - 스프링 컨테이너는 스피링의 빈(Bean)을 생성하고 관리한다. 스프링 컨테이너는 IoC Container 혹은 DI Container라고 불리는데, 이는 스프링 컨테이너가 IoC 혹은 DI를 도맡아 진행하기 때문이다. 즉, 스프링 컨테이너는 스프링 Bean들을 생성하고, 이들의 의존 관계를 연결해 주는 역할을 한다.

BeanFactory와 ApplicationContext(인프런 - 김영한님 강의)

 이러한 스프링 컨테이너는 BeanFctory와 Application Context로 나뉘는데 둘의 내용은 다음과 같다.

BeanFactory

 - 스프링 컨테이너의 최상위 인터페이스이다.

 - 스프링 빈을 관리하고 조회하는 역할을 담당한다.

ApplicationContext

 - BeanFactory 기능을 모두 상속받아 제공한다.

 - 다음과 같은 부가기능들을 제공한다.

  • 메시지 소스를 활용한 국제화 기능
  • 환경변수 - 로컬, 개발, 운영 등을 구분해서 처리
  • 애플리케이션 이벤트 관리
  • 편리한 리소스 조회

보통 스프링 컨테이너라 하면 ApplicationContext를 뜻한다. BeanFactory의 모든 기능을 상속 받는데다 편리한 부가기능을 제공하기 때문에 BeanFactory 보다는 ApplicationContext를 사용한다.

스프링 빈(Bean)이란?

스프링 공식문서에서 확인해 보자면

 스프링에서는, 스프링 IoC컨테이너에 의해 관리되고 애플리케이션의 핵심을 이루는 객체들을 Bean이로고 부른다. Bean은 스프링 IoC 컨테이너에 의해 인스턴스화되어 조립되거나 관리되는 객체를 말한다.

즉, 스프링 빈은 스프링 컨테이너에 의해 만들어지고 관리되는 객체라는 뜻이다.

[Spring] IoC,DI, 스프링 컨테이너(Container), 스프링 빈(Bean)이란? (tistory.com)

 

[Spring] IoC,DI, 스프링 컨테이너(Container), 스프링 빈(Bean)이란?

IoC(Inversion of Control)란? IoC는 제어의 역전이라는 뜻으로 프로그램의 제어 흐름을 직접 제어하는 것이 아니라 외부에서 관리하는 것을 말한다. 이전에는 개발자가 객체를 생성하고 관리하며 프로

code-lab1.tistory.com

 

728x90
반응형
728x90
반응형

● 의존관계(Dependency)란?

- 의존관계는 의존 대상 B가 변하면, 그것이 A에 영향을 미칠 때 A는 B와 의존관계라고 한다.

예를 들어 피자 가게의 요리사는 피자 레시피에 의존한다. 만약 피자 레시피가 변경된다면, 요리사는 피자를 새로운 방법으로 만들게 될 것이다. 레시피의 변화가 요리사에게 미쳤기 때문에 요리사는 레시피에 의존한다라고 할 수 있다.

public class PizzaChef {
	private PizzaRecipe pizzaRecipe;
    
    public PizzaChef() {
    	this.pizzaRecipe = new PizzaRecipe();
    }
}

PizzaChef 객체는 PizzaRecipe 객체에 의존 관계가 있다. 이러한 구조는 다음과 같은 문제점을 가지고 있다.

 -  두 클래스의 결합성이 높다.

 PizzaChef 클래스는 PizzaRecipe 클래스와 강하게 결합되어 있다는 문제점을 가지고 있다. 만약 PizzaChef가 새로운 레시피인 CheezePizzaRecipe 클래스를 이용해야 한다면 PizzaChef 클래스의 생성자를 변경해야만 한다. 마약 이후 레시피가 계속해서 바뀐다면 매번 생성자를 바꿔줘야 하는 등, 유연성이 떨어지게 된다.

 - 객체들 간의 관계가 아닌 클래스 간의 관계가 맺어진다.

객체 지향 5원칙(SOLID) 중 "추상화에 의존해야지, 구체화에 의존하면 안된다."라는 DIP원치기 존재한다. 현재 PizzaChef 클래스는 PizzaRecipe 클래스와 의존관계가 있다. 즉, PizzaChef는 클래스에 의존하고 있다. 이는 객체 지향 5원칙(SOLID)원칙을 위반하는 것이므로 PizzaChef 클래스의 변경이 어려워지게 된다.

이러한 문제점을 해결할 수 있는 것이 바로 의존관계 주입(Dependency Injection)이다.

의존관계 주입(Dependency Injection 이하 DI)이란?

 - DI는 의존관계를 외부에서 결정(주입) 하는 것을 말한다. 스프링에서는 이러한 DI를 담당하는 DI 컨테이너가 존재한다. 이 DI 컨테이너가 객체들 간의 의존관계를 주입한다.

 위의 문제점을 DI를 이용해 해결해 보자. 우선 다양한 피자 레시피를 추상화 하기 위해 PizzaRecipe를 interface로 만들자. 이후 다양한 종류의 피자 레시피는 PizzaRecipe 인터페이스를 구현하는 식으로 작성하면 된다.

public interface PizzaRecipe {}

public class CheesePizzaRecipe implements PizzaRecipe {}

이제 PizzaChef 클래스의 생성자에서 외부로부터 피자 레시피를 주입(Injection) 받도록 변경하자.

public class PizzaChef {

	private PizzaRecipe pizzaRecipe;
    
    public PizzaChef(PizzaRecipe pizzaRecipe) {
    	this.pizzaRecipe = pizzaRecipe;
    }
}

이때 스프링의 DI 컨테이너가 애플리케이션 실행 시점에 필요한 객체를 생성하여 PizzaChef클래스에 주입해주는 역할을 한다.

// DI 컨테이너에서의 동작
PizzaChef = new PizzaChef(new CheesePizzaRecipe());

// 치즈 피자 레시피에서 베이컨 피자 레시피로 바뀐다면
PizzaChef = new PizzaChef(new BaconPizzaRecipe());

이렇게 하면 피자 셰프는 피자 레시피가 바뀌더라도 생성자를 변경하지 않아도 된다. 그저 레시피가 바뀐다면 외부에서 바뀐 레시피를 주입해주기만 하면 된다.

이처럼 의존관계를 외부에서 결정하여 주입하는 것이 DI이다.

의존 관계 주입 방법

 -  스프링 공식문서에 따르면 DI는 2가지의 주요방법이 있다.

  1. 생성자 주입(Constructor-based Dependency Injection)

  2. 수정자 주입(Setter-based Dependency Injection)

 생성자 주입은 위에서 본 것 처럼 생성자를 이용하여 의존관계를 주입한 것이다. 생성자 주입은 생성자의 호출 시점에 1회 호출되는 것이 보장된다. 따라서 주입받은 객체가 변하지 않거나, 반드시 객체의 주입이 필요한 경우에 사용 할 수 있다.

public class PizzaChef {
	private PizzaRecipe pizzaRecipe;
    
    public setPizzaRecipe(PizzaRecipe pizzaRecipe) {
    	this.pizzaRecipe = pizzaRecipe;
    }
}

PizzaChef pizzaChef = new PizzaChef();

pizzaChef.setPizzaRecipe(new CheesePizzaRecipe());

이러한 수정자 주입 방법은 생성자 주입과 다르게 주입받는 객체가 변경될 가능성이 있는 경우에 사용한다.(인프런 김영한님에 의하면 거의 없다고 한다.)

이외에도 필드 주입, 일반 메서드 주입 등 방법이 있지만, 스프링은 생성자 주입을 사용하기를 권장한다.

 의존 관계 주입의 장점

- DI를 사용하면 다음과 같은 장점들이 존재한다.

 1. 결합도가 줄어든다.

 어떤 객체가 다른 객체에 의존한다는 것은, 그 의존 대상의 변화에 취약하다는 뜻이다. DI를 이요하면 주입받는 대상이 바뀔지 몰라도 해당 객체의 구현 자체를 수정할 일은 없어진다.

 2. 유연성이 높아진다.

 기존 PizzaChef 클래스는 피자 레시피를 바꾸는 것이 쉽지 않았다. 생성자 코드 자체를 변경해주어야 했지만, DI를 이용하면 생성자의 인수만 다른 피자 레시피로 바꿔주면 된다.

 3. 테스트하기 쉬워진다.

 DI를 이용한 객체는 자신이 의존하고 있는 인터페이스가 어떤 클래스로 구현되어 있는지 몰라도 된다. 따라서 테스트하기 더 쉬워진다.

 4. 가독성이 높아진다.

 

[Spring] 의존관계 주입(Dependency Injection), 의존성 주입, DI란? (tistory.com)

 

[Spring] 의존관계 주입(Dependency Injection), 의존성 주입, DI란?

의존관계(Dependency)란? 의존관계 주입(Dependency Injection)에 대하여 알아보기 전에 의존관계가 무엇인지 알아야 한다. 의존관계는 의존 대상 B가 변하면, 그것이 A에 영향을 미칠 때 A는 B와 의존관계

code-lab1.tistory.com

 

728x90
반응형
728x90
반응형

자바 프로젝트를 할 때 pom.xml과 build.gradle… 도대체 뭐가 더 좋은 걸까요?
지금부터 Maven과 Gradle의 개념, 차이, 실무 적용 팁까지 정리해드립니다!


🛠 Maven이란?

  • Apache Maven은 자바용 프로젝트 빌드 & 라이브러리 관리 도구입니다.
  • 기존에 쓰이던 Ant의 대안으로 만들어졌고, 지금도 많이 사용되고 있어요.
  • 오픈소스(아파치 라이선스)로 배포됩니다.

📌 Maven의 핵심은 POM!

<!-- pom.xml 예시 -->
<project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>demo-app</artifactId>
  <version>1.0.0</version>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
  </dependencies>
</project>
 

💡 Maven 특징 요약

항목설명
🔗 POM 기반 Project Object Model (pom.xml) 중심 구조
📦 의존성 관리 자동으로 관련된 라이브러리도 같이 가져옴
🧱 표준화된 빌드 구조 설정이 정형화되어 있어 진입장벽 낮음
🧩 플러그인 중심 빌드 compile, test, package, install 등 명확
 

⚙ Gradle이란?

  • Gradle은 차세대 빌드 시스템으로, 빌드 속도가 빠르고 유연한 스크립트 작성이 강점이에요.
  • 특히 Android 앱 공식 빌드 도구로 사용되며, 최근에는 자바 서버에서도 많이 채택됩니다.
  • Groovy 또는 Kotlin DSL 기반으로 동작합니다.

🧠 Gradle은 이렇게 생겼어요

// build.gradle 예시
plugins {
    id 'java'
    id 'application'
}

group = 'com.example'
version = '1.0.0'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
}

💡 Gradle 특징 요약

항목설명
빠른 빌드 속도 캐시 및 병렬 빌드 지원
🧑‍💻 유연한 스크립트 기반 Groovy/Kotlin으로 빌드 로직 작성
📱 Android 필수 도구 Android Studio 기본 빌드 시스템
🧩 다양한 언어 지원 Java, C/C++, Python 등 다언어 빌드 가능
 

🔍 Maven vs Gradle 비교표

항목MavenGradle
설정 방식 XML (pom.xml) Groovy/Kotlin DSL
가독성 ❌ 길고 정형화됨 ✅ 간결하고 유연함
빌드 속도 느린 편 빠름 (캐시 활용)
멀티 모듈 설정 상속 기반 설정 주입 가능 → 더 유리함
대중성 기존 레거시/공공 시스템 다수 사용 최근 Spring, Android 중심 확산
학습 난이도 초보자에게 쉬움 유연하지만 초반 진입장벽 있음
 

🧑‍💻 그럼 어떤 걸 써야 할까?

상황추천
새 프로젝트 / Spring Boot Gradle 추천 (속도 + 유연함)
안드로이드 앱 개발 무조건 Gradle
공공기관 / 보수적 환경 Maven 유지보수 쉬움
기존 Maven 프로젝트 확장 그대로 Maven 사용 or Gradle로 전환 고려
멀티 모듈 + 빠른 빌드 원하는 경우 Gradle 확실히 유리함
 

✅ 마무리 요약

  • Maven은 표준적이고 안정적인 XML 기반 빌드 시스템
  • Gradle은 속도와 유연성 모두 잡은 차세대 빌드 도구
  • 실무에서는 Gradle이 점점 Maven을 대체하는 추세
728x90
반응형
728x90
반응형

자바 개발을 하다 보면 JAR, WAR, EAR 같은 단어를 많이 듣게 됩니다.
“이게 대체 뭐고 언제 뭘 써야 하지?”
아래 이미지와 함께 구조적 차이Spring Boot에서의 선택 기준을 쉽게 설명드릴게요.


📦 JAR / WAR / EAR 구조 한눈에 보기

위 그림을 보면 구조를 직관적으로 알 수 있어요:

  • JAR: 클래스 파일과 설정이 들어간 기본 단위
  • WAR: 웹 애플리케이션 배포용, WEB-INF, web.xml 포함
  • EAR: 여러 WAR + JAR 묶은 엔터프라이즈 통합 패키지

🔍 각 파일의 의미

✅ JAR (Java ARchive)

  • 자바 클래스와 메타정보를 하나로 압축한 파일
  • 애플리케이션이나 라이브러리를 배포할 때 사용
  • .class, META-INF/manifest.mf 포함

✅ WAR (Web Application Archive)

  • 웹 애플리케이션을 위한 구조
  • JSP, Servlet 기반 웹 프로젝트를 배포하는 데 사용
  • WEB-INF/web.xml, classes, lib 등 구조 고정

✅ EAR (Enterprise ARchive)

  • 기업용 대규모 시스템 통합 배포에 사용
  • 여러 WAR, JAR 모듈을 하나로 묶은 컨테이너
  • WebLogic, JBoss 같은 Java EE 서버 환경에 최적화

🚀 Spring Boot에서의 JAR vs WAR

항목                                          JAR (기본값)                                                             WAR
WAS 포함 ✅ 내장 Tomcat 내장 ❌ 외부 WAS 필요
실행 방식 java -jar로 바로 실행 Tomcat, WebLogic 등에서 배포
설정 편의성 간단한 구조, 빠른 실행 web.xml, 복잡한 디렉토리 구조
JSP 지원 ❌ JSP 미지원 (공식 비권장) ✅ JSP 사용 가능
활용 예시 REST API, 간단한 마이크로서비스 JSP/Servlet 중심 웹 프로젝트, 전통 웹앱

 

🧩 JSP + Spring Boot = WAR 필요?

Spring Boot에서 JSP를 사용하려면 아래 조건이 필요합니다:

  1. 프로젝트를 WAR로 패키징
  2. application.properties에 아래 설정 추가:
properties
복사편집
spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp
  1. JSP 파일은 src/main/webapp/WEB-INF/views/에 위치

📌 JAR로 배포할 경우 JSP는 작동하지 않음!
→ Thymeleaf, Mustache 등 템플릿 엔진 사용을 권장합니다.


✅ 정리 요약

구분                                                           JAR                                     WAR                                    EAR
내장 Tomcat ✅ 있음 ❌ 없음 ❌ 없음
실행 단독 실행 가능 WAS 필요 Java EE 서버 필요
JSP ❌ (제한적)
구조 단순 정형화 필요 매우 복잡
Spring Boot 기본
 

🏁 마무리

  • 개인 프로젝트, 마이크로서비스, REST API → JAR 추천
  • JSP, 레거시 웹앱 → WAR 필요
  • 대규모 엔터프라이즈 시스템 → EAR (하지만 요즘은 잘 안 씀)

Spring Boot는 기본적으로 JAR 중심 구조이며,
WAR는 외부 WAS와 JSP 기반 웹앱에 한정해서 사용하는 경우가 많습니다.

728x90
반응형

+ Recent posts