1.  Todo 목록 기능 개발

등록 기능의 개발이 완료된 후 결과 화면은 목록으로 이동
목록의 경우 나중에 페이징 처리나 검색 기능이 필요하지만 시작하는 단계에서는 목록 데이터를 출력하는 수준으로 작성

 

1) TodoMapper의 개발

TodoMapper 인터페이스에 가장 최근에 등록된 TodoVO가 우선적으로 나올 수 있도록 selectAll()를 추가
public interface TodoMapper {
    String getTime();

    void insert(TodoVO todoVO);

    List<TodoVO> selectAll();
}

 

 

TodoMapper.xml에서는 selectAll()의 실제 쿼리문을 작성


  👾  <select> 태그의 경우 resultType을 지정하는 것에 주의
          ➡️  resultType은 JDBC의 ResultSet의 한 행 row을 어떤 타입의 객체로 만들것인지를 지정

    <select id="selectAll" resultType="com.example.spring_ex_01_2404.domain.TodoVO">
        SELECT * FROM tbl_todo ORDER BY tno DESC
    </select>

 

마지막으로 test 폴더 내에 작성해둔 TodoMapperTests 클래스를 이용해 테스트 코드를 작성
@Test
    public void testSelectAll() {
        List<TodoVO> todoVOList = todoMapper.selectAll();
        for (TodoVO todoVO : todoVOList) {
            log.info(todoVO);
        }

        todoVOList.forEach(item -> log.info(item));
        // for문과 foreach문 둘 중 하나 사용
    }

 

  ✓  테스트 실행 결과는 가장 나중에 추가된 데이터를 우선적으로 보여줌


2) TodoService / TodoServiceImpl의 개발

서비스 계층의 개발은 특별한 파라미터가 없는 경우 TodoMapper을 호출하는것이 전부이다. 다만, TodoMapper가 반환하는 데이터의 타입이 List<TodoVO>이기 때문에 이를 List<TodoDTO>로 변환하는 작업이 필요.

 

TodoService 인터페이스에 getAll() 기능을 추가
public interface TodoService {
    void register(TodoDTO todoDTO);

    List<TodoDTO> getAll();
}

 

TodoServiceImpl에서 getAll()을 작성

 

    @Override
    public List<TodoDTO> getAll() {
        List<TodoVO> voList = todoMapper.selectAll(); // dao에서 데이터베이스에서 들고온 VO리스트를 리턴
        List<TodoDTO> dtoList = new ArrayList<>();
        for (TodoVO todoVO: voList) {
            // 개별 VO를 DTO로 변환.
            TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
            dtoList.add(todoDTO); // DTO리스트에 저장.
        }
        return dtoList;
    }

 

💡  List<TodoVO>를 List<TodoDTO>로 변화하는 작업은 java8부터 지원하는 stream을 이용해서 각 TodoVO는 map()을 통해서 TodoDTO로 바꾸고, collect()를 이용해서 List로 묶어 줌

 

test 코드 작성후 실행
@Test
    public void testGetAll() {
        List<TodoDTO> todoDTOList = todoService.getAll();
        for (TodoDTO todoDTO: todoDTOList) {
            log.info(todoDTO);
        }

        todoDTOList.forEach(item -> log.info(item));
        // 둘 중 하나 사용
    }

 

 


3)  TodoController의 처리

TodoController의 list() 기능에서 TodoService를 처리하고 Model에 데이터를 담아서 JSP로 전달
@RequestMapping("/list")
    public void list(Model model) {
        log.info("todo list...");
        model.addAttribute("dtoList", todoService.getAll());
    }

 

  ✓  Model에는 dtoList라는 이름으로 목록 데이터를 담았기 때문에 JSP에서는 JSTL을 이용해서 목록을 출력

  ✓  화면 디자인은 부트스트랩의 tables 항목 참고

 

/WEB-INF/view/todo/list.jsp 페이지 생성

 

  • test.html 을 복사후 상단에 JSP, JSTL 설정 추가
  • <div class="card-body"> 부분을 아래와 같이 수정
<div class="card-body">
    <h5 class="card-title">Special title treatment</h5>
    <table class="table">
        <thead>
            <tr>
                <th scope="col">tno</th>
                <th scope="col">Title</th>
                <th scope="col">Writer</th>
                <th scope="col">DueDate</th>
                <th scope="col">Finished</th>
            </tr>
        </thead>
        <tbody>
        <c:forEach var="dto" items="${dtoList}">
            <tr>
                <th scope="row">${dto.tno}</th>
                <td>${dto.title}</td>
                <td>${dto.writer}</td>
                <td>${dto.dueDate}</td>
                <td>${dto.finished}</td>
            </tr>
        </c:forEach>
        </tbody>
    </table>
</div>

 


2.  Todo 조회 기능 개발

목록 화면에서 제목을 클릭했을때 "/todo/read?tno=xx"와 같이 TodoController를 호출하도록 개발

 

1)  TodoMapper 조회 기능 개발

 

TodoMapper의 개발은 selectOne()이라는 메소드를 추가


  👾  파라미터는 Long 타입으로 tno를 받도록 설계하고, TodoVO객체를 반환하도록 구성

public interface TodoMapper {
    String getTime();

    void insert(TodoVO todoVO);

    List<TodoVO> selectAll();

    TodoVO selectOne(Long tno);
}

 

TodoMapper.xml에 selectOne 추가
<select id="selectOne" resultType="com.example.spring_ex_01_2404.domain.TodoVO">
    SELECT * FROM tbl_todo WHERE tno = #{tno}
</select>

 

test 코드 작성
@Test
    public void testSelectOne() {
        TodoVO todoVO = todoMapper.selectOne(2L);
        log.info(todoVO);
    }

 


2)  TodoService / TodoServiceImpl의 개발

 

public interface TodoService {
    void register(TodoDTO todoDTO);

    List<TodoDTO> getAll();

    TodoDTO getOne(Long tno);
}
@Override
    public TodoDTO getOne(Long tno) {
        TodoVO todoVO = todoMapper.selectOne(tno);
        TodoDTO todoDTO = modelMapper.map(todoVO, TodoDTO.class);
        return todoDTO;
    }

 


3) TodoController의 개발


GET방식으로 동작하는 read() 기능을 개발

 @GetMapping("/read")
    public void read(Long tno, Model model) {
        // 1) request로 전달 받은 tno를 서비스에 전달해서 2)TodoDTO를 반환받아서 3)View 에 전달
        TodoDTO todoDTO = todoService.getOne(tno);
        log.info(todoDTO);
        model.addAttribute("dto", todoDTO);
    }

 

read.jsp 파일 생성
<div class="card-body">
    <div class="input-group mb-3">
        <span class="input-group-text">Tno</span>
        <input type="text" name="tno" class="form-control" value="${dto.tno}" readonly>
    </div>
    <div class="input-group mb-3">
        <span class="input-group-text">Title</span>
        <input type="text" name="title" class="form-control" value="${dto.title}" readonly>
    </div>
    <div class="input-group mb-3">
        <span class="input-group-text">DueDate</span>
        <input type="date" name="dueDate" class="form-control" value="${dto.dueDate}" readonly>
    </div>
    <div class="input-group mb-3">
        <span class="input-group-text">Writer</span>
        <input type="text" name="writer" class="form-control" value="${dto.writer}" readonly>
    </div>
                        
    <div class="form-check">
       <label class="form-check-label">
           Finished &nbsp;
       </label>
       <input type="checkbox" name="finished" class="form-check-input" ${dto.finished ? "checked" : ""} disabled>
    </div>
    <div class="my-4">
        <div class="float-end">
             <button type="submit" class="btn btn-primary">Modify</button>
             <button type="reset" class="btn btn-secondary">List</button>
        </div>
    </div>
</div>


4)  수정 / 삭제를 위한 링크 처리


조회 화면에 수정 / 삭제를 위해서 Modify 버튼을 클릭하면 GET 방식의 수정 / 삭제 선택이 가능한 화면으로 이동
  ➡️  자바 스크립트를 이용해서 이벤트 처리

read.jsp 의 card-body 클래스 아래에 코드 작성
<script>
    document.querySelector('.btn-primary').addEventListener('click', function () {
        self.location = '/todo/modify?tno=' + ${dto.tno};
    });
    document.querySelector('.btn-secondary').addEventListener('click', function () {
        self.location = '/todo/list';
    })
</script>

 

list.jsp의 링크 처리
<tbody>
    <c:forEach var="dto" items="${responseDTO.dtoList}">
    <tr>
        <th scope="row">${dto.tno}</th>
        <td><a href="/todo/read?tno=${dto.tno}" class="text-decoration-none">
            ${dto.title}</a></td>
        <td>${dto.writer}</td>
        <td>${dto.dueDate}</td>
        <td>${dto.finished}</td>
    </tr>
    </c:forEach>
</tbody>

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  Todo 기능 개발

등록 작업의 경우 TodoMapper ▶️ TodoService ▶️ TodoController ▶️ JSP의 순서로 처리


1) TodoMapper 개발 및 테스트

 

TodoMapper에는 TodoVO를 파라미터로 받는 insert()를 추가
public interface TodoMapper {

    String getTime();

    void insert(TodoVO todoVO);
    
}

 

TodoMapper.xml에 insert를 다음과 같이 구현
<insert id="insert">
        INSERT INTO tbl_todo (title, dueDate, writer) VALUES (#{title}, #{dueDate}, #{writer})
</insert>

 

Mybatis를 이용하면 "?" 대신에 "#{title}" 같이 파라미터를 처리, "#{title}" 부분은 PreparedStatement로 변경되면서 "?"로 처리되고, 주어진 객체의 getTitle()을 호출한 결과를 적용

 

테스트 코드를 이용해서 TodoVO의 입력을 확인
 @Test
    public void testInsert() {
        TodoVO todoVO = TodoVO.builder() // 빌더를 이용해서 TodoVO 객체를 생성
                .title("스프링 테스트")
                .dueDate(LocalDate.of(2022, 10, 10))
                .writer("user00")
                .build();
        todoMapper.insert(todoVO);
    }

 

  테스트 실행 후에 tbl_todo 테이블을 조회해서 insert가 완료되었는지 확인

 


2) TodoService와 TodoServiceImpl 클래스


TodoMapper와 TodoController 사이에는 서비스 계층을 설계해서 적용
TodoService 인터페이스를 먼저 추가하고, 이를 구현한 TodoServiceImpl을 스프링 빈으로 처리

public interface TodoService {
    void register(TodoDTO todoDTO);
}

 

  - TodoService 인터페이스에 추가한 register()는 여러 개의 파라미터 대신에 TodoDTO로 묶어서 전달 받도록 구성

 

 

TodoService 인터페이스를 구현하는 TodoServiceImpl에는 의존성 주입을 이용해서 데이터베이스 처리를 하는 TodoMapper와 DTO, VO의 변환을 처리하는 ModelMapper를 주입

 

    ⚡️  의존성 주입이 사용되는 방식은 의존성 주입이 필요한 객체의 타입을 final로 고정하고 @RequiredArgsConstructor를 이용해서 생성자를 생성하는 방식을 사용

    ⚡️  register() 에서는 주입된 ModelMapper를 이용해서 TodoDTO를 TodoVO로 변환하고 이를 TodoMapper를 통해서 insert 처리

@Log4j2
@Service
@RequiredArgsConstructor // 생성자 객체 주입. private final로 선언된 참조변수에 객체를 저장하는 생성자 작성.
public class TodoServiceImpl implements TodoService{
    private final TodoMapper todoMapper;
    private final ModelMapper modelMapper;

    @Override
    public void register(TodoDTO todoDTO) {
        // 1) todoDTO를 전달 받아 2) todoDTO를 todoVO로 변환 후 3) dao의 insert()호출
        log.info(todoDTO);
        TodoVO todoVO = modelMapper.map(todoDTO, TodoVO.class);
        log.info(todoVO);
        todoMapper.insert(todoVO);
    }
}

 

service 패키지는 root-context.xml에서 component-scan 패키지로 추가

<context:component-scan base-package="com.example.spring_ex_01_2404.service"/>

 


3)  TodoService 테스트


서비스 계층에서 DTO를 VO로 변환하는 작업을 처리하기 때문에 가능하면 테스트를 진행해서 문제가 없는지 확인하는 것이 좋음
test 폴더내에 service 관련 패키지를 생성하고 TodoServiceTest 클래스를 작성

@Log4j2
@ExtendWith(SpringExtension.class)
@ContextConfiguration(locations = "file:src/main/webapp/WEB-INF/root-context.xml")
class TodoServiceTests {
    @Autowired
    private TodoService todoService;

    @Test
    public void testRegister() {
        TodoDTO todoDTO = TodoDTO.builder()
                .title("test...")
                .dueDate(LocalDate.now())
                .writer("user1")
                .build();
        todoService.register(todoDTO);
    }
}


4)  TodoController의 GET / POST 처리


서비스 계층까지 문제 없이 동작하는 것을 확인했다면 스프링 MVC를 처리
입력할 수 있는 화면을 위해 controller 패키지의 TodoController를 확인

 

TodoController에 GET 방식으로 '/todo/register'가 가능한지 확인
@Log4j2
@Controller
@RequestMapping("/todo")
@RequiredArgsConstructor
public class TodoController {

    @GetMapping("/register")
    public void register() {
        log.info("todo register...");
    }
}

 

/WEB-INF/view/todo/ 폴더에 register.jsp는 test.html을 복사해서 구성. 상단에 JSP 관련 설정을 추가.
register.jsp에 class 속성이 "card-body"로 지정된 부분의 코드를 수정
입력하는 화면의 디자인은 https://getbootstrap.com/docs/5.1/forms/form-control/

 

Form controls

Give textual form controls like <input>s and <textarea>s an upgrade with custom styles, sizing, focus states, and more.

getbootstrap.com

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<div class="card-body">
    <form  method="post">
        <div class="input-group mb-3">
            <span class="input-group-text">Title</span>
            <input type="text" name="title" class="form-control" placeholder="Title">
        </div>
        <div class="input-group mb-3">
            <span class="input-group-text">DueDate</span>
            <input type="date" name="dueDate" class="form-control">
        </div>
        <div class="input-group mb-3">
            <span class="input-group-text">Writer</span>
            <input type="text" name="writer" class="form-control" placeholder="Writer">
        </div>
        <div class="my-4">
            <div class="float-end">
                <button type="submit" class="btn btn-primary">Submit</button>
                <button type="reset" class="btn btn-secondary">Reset</button>
            </div>
        </div>
    </form>
</div>

 

 POST 방식의 처리


    1.  register.jsp의 <form method="post"> 태그에 의해서 submit 버튼을 클릭하면 POST 방식으로 "title, dueDate, writer"를 전송하게 됨
    2.  TodoController에서는 TodoDTO로 바로 전달된 파라미터의 값들을 수집
    3.  POST 방식으로 처리된 후에는 "/register/list"로 이동해야 하므로 "redirect:/todo/list"로 이동할 수 있도록 문자열을 반환

@PostMapping("/register")
public String registerPOST(TodoDTO todoDTO) {
    log.info("POST todo register");
    log.info(todoDTO);
    return "redirect:/todo/list";
}

 

  ✓  한글 문제가 있기는 하지만 브라우저에서 입력한 데이터들이 수집되고 /todo/list로 이동하는 기능에 문제가 없는 것을 확인

 


2.  한글 처리를 위한 필터 설정

서버의 한글 처리에 대한 설정은 스프링 MVC에서 제공하는 필터로 쉽게 처리할 수 있음

 

web.xml에 필터에 대한 설정을 추가
    <filter>
        <filter-name>encoding</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>encoding</filter-name>
        <servlet-name>appServlet</servlet-name>
    </filter-mapping>

 

  ✓  web.xml의 설정을 서버를 재시작해야 올바르게 반영되므로 톰캣을 재시작하고 한글 처리를 확인


3.  @Valid를 이용한 서버사이드 유효성 검증

유효성 검증


과거의 웹 개발에는 자바 스크립트를 이용하여 브라우저에서만 유효성 검사를 진행하는 방식이 많았지만, 모바일과 같이 다양한 환경에서 서버를 이용하는 현재에는 브라우저를 사용하는 프론트쪽에서의 검증과 더불어 서버에서도 입력되는 값들을 검증하는 것이 좋음. 이러한 검증 작업은 컨트롤러에서 진행하는데 스프링MVC의 경우 @Valid와 BindingResult라는 존재를 이용해서 간단하게 처리할 수 있음


  ⚡️  스프링 MVC에서 검증을 처리하기 위해서는 hibernate-validate 라이브러리가 필요 (build.gradle 에 추가)

// https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator
implementation 'org.hibernate.validator:hibernate-validator:6.2.1.Final'

 

hibernate-validate를 이용해서 사용하는 대표적인 어노테이션
어노테이션 설명 어노테이션 설명
@NotNull Null 불가 @Null Null만 입력 가능
@NotEmpty Null 빈 문자열 불가 @NotBlank Null 빈 문자열, 스페이스만 있는 문자열 불가
@SIze(min=, max=) 문자열, 배열 등의 크기가
만족하는가
@Pattern(regex=) 정규식을 만족하는가
@Max(num) 지정 값 이하인가 @Min(num) 지정 값 이상인가
@Future 현재 보다 미래인가 @Past 현재 보다 과거인가
@Positive 양수만 가능 @PositiveOrZero 양수와 0만 가능
@Negative  음수만 가능 @NegativeOrZero 음수와 0만 가능

 

 

1) TodoDTO 검증하기

 

TodoDTO에 간단한 어노테이션 적용
@ToString
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TodoDTO {
    private Long tno;
    
    @NotEmpty
    private String title;
    
    @Future
    private LocalDate dueDate;
    
    private boolean finished;
    
    @NotEmpty
    private String writer;
}

 

 

TodoController에서 POST 방식으로 처리할 때 이를 반영하도록 BindingResult와 @Valid 어노테이션을 적용
 @PostMapping("/register")
    public String registerPOST(@Valid TodoDTO todoDTO, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
        log.info("POST todo register");

        if (bindingResult.hasErrors()) {
            log.info("has error...");
            redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
            return "redirect:/todo/register";
        }

        log.info(todoDTO);
        return "redirect:/todo/list";
    }

 

  ✓  TodoDTO 에는 @Valid를 적용하고, BindingResult 타입을 파라미터로 새롭게 추가

    ➡️  BindingResult는  스프링이 제공하는 검증 오류를 보관하는 객체

  ✓  registerPOST() 에서는 hasErrors()을 이용해서 검증에 문제가 있다면 다시 입력화면으로 리다이렉트되도록 처리


  ✓  처리 과정에서 잘못된 결과는 RedirectAttributes의 addFlashAttribute()를 이용해서 전달

    ➡️ flash 속성에 객체를 저장, 요청 매개 변수(requestparameters)로 값을 전달하지않고 객체로 값을 그대로 전달. 일회성으로 한번 사용하면 Redirect후 값이 소멸


  ✓  TodoDTO의 writer는 @NotEmpty가 적용되어 있으므로 항목을 입력하지 않고 submit을 하면 다시 입력화면으로 돌아감

 

 

 

 

 


 

2) JSP에서 검증 에러 메시지 확인하기

 

register.jsp에 검증된 결과를 확인하기 위해 상단에 태그 라이브러리 추가
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>

 

자바스크립트 객체를 생성해 둔 후 필요할 때 화면에서 처리
<form>태그가 끝난 후에 <script> 태그를 추가
<script>
    const serverValidResult = {};
    <c:forEach items="${errors}" var="error">
    serverValidResult['${error.getField()}'] = '${error.defaultMessage}';
    </c:forEach>
    console.log(serverValidResult);
</script>

 

  ✓  자바스크립트를 이용해서 오류 객체를 생성하면 나중에 화면에서 자유롭게 처리할 수 있다는 장점이 있음

  ✓  아무것도 입력하지 않은 상태에서 submit을 하면 다음과 같은 코드가 출력

아무 것도 입력하지 않았을 때와 과거 날짜를 입력했을 때 콘솔 창 출력 결과

 


4.  Todo 등록 기능 완성

입력값의 검증까지 끝났다면 최종적으로 TodoService를 주입하고, 연동하도록 구성

 

TodoController의 클래스 선언부에서 TodoService를 주입
@Log4j2
@Controller
@RequestMapping("/todo")
@RequiredArgsConstructor
public class TodoController {
    private final TodoService todoService;

 

registerPOST()에서는 TodoService의 기능을 호출하도록 구성
 @PostMapping("/register")
    public String registerPOST(@Valid TodoDTO todoDTO, BindingResult bindingResult, RedirectAttributes redirectAttributes) {
        log.info("POST todo register");

        if (bindingResult.hasErrors()) {
            log.info("has error...");
            redirectAttributes.addFlashAttribute("errors", bindingResult.getAllErrors());
            return "redirect:/todo/register";
        }

        log.info(todoDTO);
        todoService.register(todoDTO); // 호출 코드 생성
        return "redirect:/todo/list";
    }

 

 

 

 

모든 기능의 개발이 완료 되었다면 등록 후에 "/todo/list"로 이동하게 됨. 아직 "/todo/list"의 개발은 완료되지 않았으니 데이터베이스를 이용해서 최종 확인

 

 

 

 

 

 

 

 

 

 

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  스프링 Web MVC의 특징

출처 :&nbsp;https://www.logicbig.com/tutorials/spring-framework/spring-web-mvc/spring-mvc-intro.html
출처 :&nbsp;https://tvd12.com/posts/spring-mvc-request-lifecycle

 

스프링 Web MVC는 기본적으로 서블릿 API를 좀 더 추상화된 형태로 작성된 라이브러리지만, 서블릿 / JSP를 사용할 때 필요한 많은 기능들을 기본적으로 제공해서 개발의 생산성과 안정성을 높여줌

 

  📍 스프링 Web MVC는 이름에서 알 수 있듯이 Web MVC 패턴으로 구현된 구조
  📍 스프링 Web MVC가 기존 구조에 약간의 변화를 주는 부분

Front-Contoller 패턴을 이용해 모든 흐름의 사전 / 사후 처리를 가능하도록 설계된 점
어노테이션을 적극적으로 활용해서 최소한의 코드로 많은 처리가 가능하도록 설계된 점
HttpServletRequest / HttpServletResponse를 이용하지 않아도 될 만큼 추상화된 방식으로 개발 가능

 

구성요소 설명
DispatcherServlet 클라이언트 요청을 전달받아 요청에 맞는 컨트롤러가 리턴한 결과값을 View에 전달하여 알맞은 응답을 생성
HandlerMapping 클라이언트의 요청 URL을 어떤 Controller가 처리할 지 결정
Controller 클라이언트의 요청을 처리한 뒤, 결과를 DispatcherServlet에게 리턴
ModelAndView Controller가 처리한 결과 정보 및 View 선택에 필요한 정보를 담음
ViewResolver Controller의 처리 결과를 생성할 View를 결정
View Controller의 처리 결과 화면을 생성

 

출처 : https://velog.io/@codemcd/


1)  DispatcherServlet 과 FrontController

스프링 MVC에서 가장 주요한 사실은 모든 요청 Request이 반드시 DispatcherServlet이라는 존재를 통해서 실행
  ✓ 
객체지향에서 이렇게 모든 흐름을 하나의 객체를 통해서 진행되는 패턴을 퍼사드 facade 패턴이라 하는데  구조에서는 Front-Controller 패턴 이라도 부름
  ✓ 
Front-Controller 패턴을 이용하면 모든 요청이 반드시 하나의 객체를 지나서 처리되기 때문에 모든 공통적인 처리를 프론트 컨트롤러에서 처리할 수 있게 됨

 

 

 

👾  스프링 MVC에서는 DispatcherServlet이라는 객체가 프론트 컨트롤러의 역할을 수행
👾  프론트 컨트롤러가 사전 / 사후에 대한 처리를 하게 되면 중간에 매번 다른 처리를 하는 부분만 별도로 처리하는 구조를 만들게 됨 

     ➡️  스프링 MVC 에서는 이를 컨트롤러라고 하는데 @Controller를 이용해서 처리

 

 

 


2)  스프링 MVC 사용하기

👻  spring-webmvc 라이브러리는 미리 추가되었으므로 스프링 MVC관련 설정을 추가 

 

㉮  스프링 MVC관련 설정을 추가하기 위해 WEB-INF 폴더에 servlet-context.xml 파일을 생성
  ✓  root-context.xml을 이용할 수도 있지만, 일반적으로는 구조를 분리하듯이 웹을 다루는 영역이라 별도의 설정 파일을 이용하는 것이 일반적이다.

 

㉯  프로젝트 내부의 webapp 폴더 아래에 'resources'라는 폴더를 하나 더 생성해 둠

  ✓  나중에 정적 파일들(html, css, js, 이미지등)을 서비스하기 위한 경로

 

 servlet-context.xml  설정 코드
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/mvc
        https://www.springframework.org/schema/mvc/spring-mvc.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
        
    <mvc:annotation-driven/>
    <mvc:resources mapping="/resources/**" location="/resources/"/>
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="/WEB-INF/view/"/>
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

 

  ⚡️  <mvc:annotation-driven> 설정 : 스프링 MVC 설정을 어노테이션 기반으로 처리한다는 의미와 스프링 MVC의 여러 객체들을 자동으로 스프링의 빈으로 등록하게 하는 기능을 의미
  ⚡️  <mvc:resources> 설정 : 이미지나 html 파일과 같이 정적인 파일의 경로를 지정
        ✓  /resources 경로로 들어오는 요청은 정적 파일을 요구하는 것이라고 생각하고 스프링 MVC에서 처리하지 않는다는 의미
        ✓  location 속성 값은 webapp 폴더에서 만들어둔 폴더를 의미
        ✓  InternalResourceViewResolver라는 이름의 클래스로 빈이 설정되어 있는데,
             InternalResourceViewResolver는 스프링 MVC에서 제공하는 뷰(view)를 어떻게 결정하는지에 대한 설정을 담당
        ✓  prefix와 suffix는 뷰를 이용할때 파일명만 주어지면 조합해서 '/WEB-INF/view/파일명.jsp' 같은 형식을 만들 수 있음

 

 

web.xml의 DispatcherServlet 설정

 

스프링 MVC를 실행하려면 front controller 역할을 하는 DispatcherServlet을 설정해야하는데,  web.xml을 이용해서 처리

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/root-context.xml</param-value>
    </context-param>
    
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>

    <servlet>
        <servlet-name>appServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/servlet-context.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    
    <servlet-mapping>
        <servlet-name>appServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

 

  ⚡️  <servlet> 설정과 <servlet-mapping> 설정이 추가
        ✓  <servlet> 설정은 DispatcherServlet을 등록하는데 DispatcherServlet이 로딩할 때 servlet-context.xml을 이용하도록 설정
        ✓  load-on-startup 설정의 경우 톰캣 로딩 시에 클래스를 미리 로딩해 두기 위한 설정
        ✓  <servlet-mapping> 설정모든 경로의 요청에 대한 처리를 담당하기 때문에 '/'로 지정

 


3)  스프링 MVC 컨트롤러 

스프링 MVC 컨트롤러는 전통적인 자바의 클래스 구현 방식과 다름

스프링 MVC의 컨트롤러 특징

 

  ✓  상속이나 인터페이스를 구현하는 방식을 사용하지 않고 어노테이션만으로 처리가 가능
  ✓  오버라이드 없이 필요한 메소드들을 정의
  ✓  메소드의 파라미터를 기본 자료형이나 객체 자료형을 마음대로 지정
  ✓  메소드의 리턴타입도 void, String, 객체 등 다양한 타입을 사용할 수 있음

 

㉮  controller 패키지를 추가하고 SampleController 클래스를 추가

@Log4j2
@Controller
public class SampleController {

    @GetMapping("/hello")
    public void hello() {
        log.info("hello(_)...");
    }
}

 

  ⚡️  몇 가지 특이한 어노테이션들이 사용
      ✓  @Controller는 해당 클래스가 스프링 MVC에서 컨트롤러 역할을 한다는 것을 의미하고 스프링의 빈으로 처리되기 위해서 사용
      ✓  @GetMapping은 GET 방식으로 들어오는 요청을 위해 사용

 


 

A) servlet-context.xml의 component-scan


controller 패키지에 존재하는 컨트롤러 클래스들을 스프링으로 인식하기 위해서는 해당 패키지를 스캔해서 @Controller 어노테이션이 추가된 클래스들의 객체들을 스프링이 빈으로 설정되게 만들어야 함

servlet-context.xml에 component-scan을 적용
<context:component-scan base-package="com.example.spring.controller"/>

 

WEB-INF에 view 폴더 생성후 hello.jsp 작성
<html>
<head>
    <title>Title</title>
</head>
<body>
  <h1>Hello JSP</h1>
</body>
</html>


 

B) @RequestMapping과 파생 어노테이션들


스프링 컨트롤러에서 가장 많이 사용하는 어노테이션은 @RequestMapping
  ⚡️  @RequestMapping은 말 그대로 '특정한 경로의 요청 Request을 지정'하기 위해서 사용
  ⚡️  컨트롤러 클래스의 선언부에도 사용할 수 있고, 컨트롤러의 메서드에도 사용할 수 있음

 

controller 패키지에 TodoController 클래스 추가
@Log4j2
@Controller
@RequestMapping("/todo")
public class TodoController {
    @RequestMapping("/list")
    public void list() {
        log.info("todo list...");
    }

    @RequestMapping(value = "/register", method = RequestMethod.GET)
    public void register() {
        log.info("todo register...");
    }
}

 

  ⚡️  TodoController의 @RequestMapping의 value 값은 '/todo'이고 list()는 '/list'이므로 최종경로는 '/todo/list' 
  ⚡️  JSP 파일이 없어서 에러가 나지만 로그 출력은 확인

 

  ⚡️  @RequestMapping을 이용하는 것만으로도 여러 개의 컨트롤러를 하나의 클래스로 묶을 수 있고, 각 기능을 메소드 단위로 설계할 수 있게 되므로 실제 개발에서 많은 양의 코드를 줄일 수 있음

  ⚡️  @RequestMapping에는 method 속성을 이용해서 GET / POST 방식을 구분해서 처리했지만, 스프링 4버전 이후에는 @GetMapping, @PostMapping 어노테이션이 추가되면서 GET / POST 방식을 구분해서 처리할 수 있게 됨
      ➡️  예를 들어 Todo 등록의 경우 GET 방식으로 '/todo/register'를 이용하면 입력 가능한 화면을 보여주고, POST방식은 처리를 해야 함

@Log4j2
@Controller
@RequestMapping("/todo")
public class TodoController {
    @RequestMapping("/list")
    public void list() {
        log.info("todo list...");
    }

//    @RequestMapping(value = "/register", method = RequestMethod.GET)
    @GetMapping("/register")
    public void register() {
        log.info("todo register...");
    }

    @PostMapping("/register")
    public void registerPOST(TodoDTO todoDTO) {
        log.info("POST todo register...");
        log.info(todoDTO);
    }
}

 


C)  파라미터 자동 수집과 변환


👩🏻‍💻  스프링 MVC가 인기를 끌게 된 여러 이유 중에는 개발 시간을 단축할 수 있는 편리한 기능들이 많기 때문
👩🏻‍💻  개발자들에게 가장 필요한 파라미터 자동 수집 기능

 

  ⚡️  파라미터 자동 수집 기능은 간단히 말해서 DTO나 VO등을 메서드의 파라미터로 설정하면 자동으로 전달되는

         HttpServletRequest의 파라미터들을 수집해 주는 기능
  ⚡️  단순히 문자열만이 아니라 숫자도 가능하고, 배열이나 리스트, 첨부 파일도 가능

 

파라미터 수집은 다음과 같은 기준으로 동작


  ✓  기본 자료형의 경우는 자동으로 형 변환처리가 가능
  ✓  객체 자료형의 경우는 setXXX()의 동작을 통해서 처리
  ✓  객체 자료형의 경우 생성자가 없거나 파라미터가 없는 생성자가 필요

 

단순 파라미터의 자동 수집

 

  📍 SampleController에 ex1() 추가

    @GetMapping("/ex1")
    public void ex1(String name, int age) {
        log.info("ex1() ...");
        log.info("name: " + name);
        log.info("age: " + age);
    }

 

  ✓  문자열 name과 int 타입의 age 선언
  ✓  브라우저를 이용해서 name과 age를 지정하면 'ex1?name=aaa&age=16'와 같이 호출 되었을 때 자동으로 처리되는 것 확인

 

 

  

 

 

[ 내용 참고 : IT 학원 강의 ]


1.  스프링의 시작 

스프링 프레임워크는 웹이라는 제한적 용도로만 쓰이는 것이 아니라 객체지향의 의존성 주입 dependency injection 기법을 적용할 수 있는 객체지향 프레임워크

 

  •   스프링 프레임워크는 로드 존슨이 2002년도에 집필했던 'J2EE 설계 및 개발 wrox'이라는 책의 예제 코드에서 시작되었는데 말 그대로 효과적이고 가볍게 J2EE를 이용할 수 있다는 것을 증명하면서 예제의 코드를 발전시킨 것
  • 2000년 당시 자바 진영에서 JavaEE의 여러가지 스펙을 정의하고 비대해지는 동안 스프링 프레임워크는 반대로 '경량 light weight 프레임워크'를 목표로 만들어짐
  • 스프링이 등장할 때 여러 종류의 프레임워크들이 비슷한 사상(경량화, 단순화)으로 등장했지만 다른 프레임워크들과 달리 스프링 프레임워크는 개발과 설계 전반에 관련된 문제들을 같이 다루었기 때문에 결론적으로 가장 성공한 프레임워크로 기록
  • 스프링 프레임워크는 가장 중요한 '코어core' 역할을 하는 라이브러리와  여러 개의 추가적인 라이브러리를 결합하는 형태로 프로젝트를 구성. 가장 대표적으로 웹 MVC구현을 쉽게 할 수 있는 'Spring Web MVC'나 JDBC 처리를 쉽게 할 수 있는 'MyBatis'를 연동하는 'mybatis-spring'과 같은 라이브러리가 그러한 예. 

2.  의존성 주입 

스프링이 객체지향 구조를 설계할 때 받아들인 개념은 '의존성 주입 dependency injection'이라는 사상.

    ✓ 의존성 주입은 어떻게 하면 '객체와 객체 간의 관계를 더 유연하게 유지할 것인가?'에 대한 고민에서 나옴

    ✓ 즉 의존성이란 하나의 객체가 자신이 해야 하는 일을 하기 위해서 다른 객체의 도움이 필수적인 관계를 의미

    ✓ 스프링 프레임워크는 바로 이런 점을 프레임워크 자체에서 지원. 다양한 방식으로 필요한 객체를 찾아서 사용할 수 있도록 XML 설정이나 자바 설정 등을 이용

 


3.  ApplicationContext 와 빈 Bean

👻  서블릿이 존재하는 공간을 서블릿 콘텍스트 ServletContext라고 했던 것 처럼, 스프링에서는 빈 Bean 이라고 부르는 객체들을 관리하기 위해서 ApplicationContext 라는 존재를 활용

코드의 경우 ApplicationContext는 root-context.xml을 이용해서 스프링이 실행

ApplicationContext 객체가 생성

root-context.xml을 읽으면 SampleService와 SampleDAO가 해당 클래스의 객체를 생성해서 관리

테스트를 실행하면 @Autowired가 처리된 부분에 맞는 타입의 빈 Bean 이 존재하는지를 확인

이를 테스트 코드 실행 시에 주입

멤버변수에 직접 @Autowired를 선언하는 방식을 '필드 주입 Field Injection' 방식

 


4.  SampleDAO 주입하기 

 

@Autowired를 이용하면 필요한 타입을 주입받을 수 있다는 사실을 이용해서 SampleService를 다음과 같이 변경

 

 

 

📌  Lombok의 @ToString을 적용한 부분과 SampleDAO의 변수로 선언하고 @Autowired를 적용

📌  테스트 코드를 실행하면 SampleService 객체 안에 SampleDAO 객체가 주입된 것을 확인

 

 

 

 

 

👾  스프링을 이용할 때는 클래스를 작성하거나 객체를 직접 생성하지 않음

👾 이 역할은 스프링 내부에서 이루어지며 ApplicationContext가 생성된 객체들을 관리

  • 서블릿의 url 매핑의 경우 web.xml에 <servlet>이라는 태그를 이용해서 서블릿 클래스의 이름과 경로를 전부 기록해야 했지만, 최근에는 @WebServlet 어노테이션이 이를 대신하는 것처럼, 스프링도 비슷한 방식으로 발전, 초기 스프링 버전에서는 XML 파일에 <bean>이라는 것을 이용해서 설정하는 방식이 2.5버전 이후에 어노테이션 형태로 변화되면서 예전에 비해 편리하게 설정이 가능

5.  @Repository, @Service 

👩🏻‍💻  서블릿에서도 @WebServlet이나 @WebFilter와 같이 다양한 어노테이션이 존재하듯이 스프링 프레임워크는 애플리케이션 전체를 커버하기 때문에 다양한 종류의 어노테이션을 사용하도록 작성 

 

@Controller      : MVC의 컨트롤러를 위한 어노테이션
@Service           : 서비스 계층의 객체를 위한 어노테이션
@Repository    : DAO와 같은 객체를 위한 어노테이션
@Component   : 일반 객체나 유틸리티 객체를 위한 어노테이션

 

💡  DAO(Data Access Object)   "실제로 DB의 데이터에 접근하는 객체"
  •  DAO는 Service와 DB를 연결하는 역할을 하며, 실제로 DB에 접근하여 data를 삽입, 삭제, 조회, 수정 등 CRUD 기능을 수행

💡 DTO(Data Transfer Object)   "계층 간 데이터 교환을 위한 객체"

  • 로직을 가지지 않고 getter/setter 메소드만 가진 순수한 데이터 객체 클래스(Java Beans)로 DB에서 데이터를 얻어 Service나 Controller 등으로 보낼 때 사용

💡 VO(Value Object)   "변경 불가능하며 오직 읽기만 가능 (Read-Only)"

  • DTO는 setter를 가지고 있어 값을 변경할 수 있지만, VO는 getter만을 가지기 때문에 읽기만 가능하고 수정은 불가능

 

출처: https://hstory0208.tistory.com/entry/Spring-DAO-DTO-VO란-각각의-개념에-대해-알아보자 

 

 

👩🏻‍💻  스프링이 사용하는 어노테이션의 경우 웹 영역뿐만 아니라 애플리케이션 전체에 사용할 수 있는 객체들을 포함

👩🏻‍💻  어노테이션을 이용하게 되면 스프링 설정은 <bean>이 아니라 '해당 패키지를 조사해서 어노테이션들을 이용'하는 설정으로 변경

 

설정 변경 전과 후

 

📌  기존 설정과 비교해 보면 XML 위쪽의 xmlns 네임 스페이스가 추가되고 schemaLocation이 변경

📌  내용에는 component-scan이 추가되었는데 속성값으로는 패키지를 지정 

📌  'component-scan'은 해당 패키지를 스캔해서 스프링의 어노테이션들을 인식

 


 

 

 

🎃  SampleDAO는 해당 클래스의 객체가 스프링에서 빈 Bean으로 관리될 수 있도록 @Repository 라는 어노테이션을 추가

 

 

 

 

 

 

 

 

 

🎃  SampleService에는 @Service 어노테이션을 추가

 

 

 

 

 

 

 


 

생성자 주입 방식 

 

👾  초기 스프링에서는 @Autowired를 멤버 변수에 할당하거나, Setter를 작성하는 방식을 많이 이용해 왔지만, 스프링 3 이후에는 생성자 주입이라고 부르는 방식을 더 많이 활용.

👾  생성자 주입 방식은 객체를 생성할 때 문제가 발생하는 지를 미리 확인할 수 있기 때문에 필드 주입이나 Setter 주입 방식 보다 선호되는 방식 

👾  Lombok에서는 생성자 주입을 보다 간단하게 작성할 수 있는데 @RequiredArgsConstructor를 이용해서  필요한 생성자 함수를 자동으로 작성할 수 있음

 

 

 

 

 

💫  생성자 주입 방식은 다음과 같은 규칙으로 작성

   

    - 주입 받아야 하는 객체의 변수는 final로 작성

     - 생성자를 이용해서 해당 변수를 생성자의 파라미터로 지정

 

 

 

 

 

 

 


6.  인터페이스를 이용한 느슨한 결합 

스프링이 의존성 주입을 가능하게 하지만  '좀 더 근본적으로 유연한 프로그램을 설계하기 위해서는 인터페이스를 이용'해서 나중에 다른 클래스의 객체로 쉽게 변경할 수 있도록 하는 것이 좋음.

    ⚡️  예를 들어 코드에서 SampleDAO를 다른 객체로 변경하려면 결론적으로 SampleService 코드 역시 수정해야 함. 추상화된 타입을 이용하면 이러한 문제를 피할 수 있는데 가장 대표적인 것이 인터페이스. 인터페이스를 이용하면 실제 객체를 모르고 타입만을 이용해서 코드를 작성하는 일이 가능해 짐. 

 

1)  SampleDAO를 인터페이스로 변경하기  

 

 

 

🎃  클래스로 작성된 SampleDAO을 인터페이스로 수정. @Repository 삭제

🎃  SampleService는 SampleDAO라는 인터페이스를 보게 되었지만 코드상의 변경은 필요하지 않음

 

 

 

 

 

 

 

 

🎃 SampleDAO 인터페이스는 구현 코드가 없기 때문에 구현한 클래스를 SampleDAOImpl 이라는 이름으로 선언

🎃 SampleDAOImpl에는 @Repository를 이용해서 해당 클래스의 객체를 스프링의 빈 Bean으로 처리되도록 구성

 

 

 

 

 

SampleService의 입장에서는 인터페이스만 바라보고 있기 때문에 실제 객체가 SampleDAOImpl의 인스턴스인지 알수 없지만, 코드를 작성하는 데에는 아무런 문제가 없음. 이처럼 객체와 객체의 의존 관계의 실제 객체를 몰라도 가능하게 하는 방식을 '느슨한 결합 Loose coupling'이라고 함. 느슨한 결합을 이용하면 나중에 SampleDAO 타입의 객체를 다른 객체로 변경하더라도 SampleService 타입을 이용하는 코드를 수정할 일이 없기 때문에 보다 유연한 구조가 됨. 


2) 다른 SampleDAO 객체로 변경해 보기 

 

 

🎃  예를 들어 특정한 기간에만 SampleDAO를 다른 객체로 변경해야 되는 경우 EventSampleDAOImpl이라는 클래스를 작성

 

 

 

 

 

 

 

 

이렇게 되면 SampleService에 필요한 SampleDAO 타입의 빈 Bean이 두 개 SampleDAOImpl, EventSampleDAOImpl 가 되기 때문에 스프링 입장에서는 어떤 것을 주입해야 하는지 알 수 없음테스트 코드를 실행하면 어떤 클래스의 객체를 사용해야 하는지 알 수 없으므로 다음과 같은 에러가 발생

 

Error creating bean with name 'sampleService' defined in file [E:\work\dev\projects\projects-spring intellij\springex\build\classes\java\main\com\nomadlab\springex\sample\SampleService.class]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.nomadlab.springex.sample.SampleDAO' available: expected single matching bean but found 2: eventSampleDAOImpl,sampleDAOImpl 

 

 

스프링이 기대하는 것은 SampleDAO 타입의 객체가 하나 single 이길 기대했지만 2개가 발견. 

이를 해결하는 가장 간단한 방법은 두 클래스 중에 하나를 @Primary라는 어노테이션으로 지정해 주는 것

 


 

3) @Qualifier 이용하기 

 

👾  @Primary를 이용하는 방식 외에도 @Qualifier를 이용하는 방식도 있음

👾  @Qualifier는 이름을 지정해서 특정한 이름의 객체를 주입받는 방식

 

  📌  Lombok과 @Qualifier를 같이 이용하기 위해서는 src/main/java 폴더에 lombok.config 파일을 생성

 

 

 

 

🎃  동작을 확인하기 위해서 SampleDAOImpl과 EventSampleDAOImpl 에는 @Qualifier를 적용. 

🎃  SampleDAOImpl 에는 normal 이라는 이름을 지정 

 

 

 

 

 

 

 

🎃  EventSampleDAOImpl에는 event 라는 이름을 지정 

 

 

 

 

 

 

 

 

 

 

 

🎃  SampleService에는 특정한 이름의 객체를 사용하도록 수정

 

 

 

 

 

 

 

    🎃  normal 이름을 가진 SampleDAOImpl이 주입되는 것을 확인

 


4)  스프링의 빈 Bean 으로 지정되는 객체들 

 

👾  스프링 프레임워크를 이용해서 객체를 생성하고 의존성 주입을 이용할 수 있다는 사실을 알았지만 작성되는 모든 클래스의 객체가 스프링의 빈 Bean으로 처리되는 것은 아님

👾  스프링의 빈 Bean으로  등록되는  객체들은 쉽게 말해서 '핵심 배역'을 하는 객체들로 오랜 시간 동안 프로그램 내에 상주하면서 중요한 역할을 하는 '역할' 중심의 객체들

👾  반대로 DTO나 VO와 같이 '역할' 보다는 '데이터'에 중심을 두고 설계된 객체들은 스프링의 빈 Bean을 등록하지 않음

      특히 DTO의 경우 생명주기가 굉장히 짧고, 데이터 보관이 주된 역할이기 때문에 스프링에서 빈 Bean으로 처리하지 않음


5) XML이나 어노테이션으로 처리하는 객체 

 

👾  빈 Bean 으로 처리할 때 XML 설정을 이용할 수도 있고, 어노테이션을 처리할 수도 있지만, 이에 대한 기준은 '코드를 수정할 수 있는가'로 판단

👾  예를 들어 jar 파일로 추가되는 클래스의 객체를 스프링의 빈Bean으로 처리해야 한다면 해당 코드가 존재하지 않기 때문에 어노테이션을 추가할 수가 없다는 문제가 생김. 이러한 객체들은 XML에서 <bean>을 이용해서 처리하고, 직접 처리되는 클래스는 어노테이션을 이용하는 것이 좋음. 


7.   웹 프로젝트를 위한 스프링 준비 

💡  스프링의 구조를 보면 ApplicatonContext라는 객체가 존재하고 빈으로 등록된 객체들은  ApplicatonContext 내에 생성되어 관리되는 구조.

    ⚡️  이렇게 만들어진 ApplicatonContext가 웹 애플리케이션에 동작하려면 웹 애플리케이션이 실행될 때  스프링을 로딩해서 해당 웹 애플리케이션 내부에 스프링의 ApplicatonContext를 생성하는 작업이 필요하게 되는데 이를 위해서 web.xml을 이용해서 리스너를 설정

    ⚡️  스프링 프레임워크의 웹과 관련된 작업은 'spring-webmvc'라이브러리를 추가해야만 설정이 가능

 

 

 

 

▶️  build.gradle파일에 spring-mvc 라이브러리를 추가

 

 

 

 

▶️  WEB-INF 폴더 아래 web.xml에  <listener>설정과 <listener>에 필요한 <context-param>을 추가

 

 

 

 

💫  설정이 추가된 후에 톰캣을 실행하면 스프링과 관련된 로그가 기록되면서 실행 


8.  DataSource 구성하기 

톰캣과 스프링이 연동되는 구조를 완성했다면 데이터베이스 관련 설정을 추가

 

  📌  build.gradle에 MySQL과 HikariCP 관련 라이브러리를 추가

    // https://mvnrepository.com/artifact/mysql/mysql-connector-java
    implementation 'mysql:mysql-connector-java:8.0.33'

    // https://mvnrepository.com/artifact/com.zaxxer/HikariCP
    implementation 'com.zaxxer:HikariCP:5.0.1'

 

 

   📌  root-context.xml에 HikariCP 설정하기 

    <context:component-scan base-package="com.example.sping_class.sample" />

    <bean id="hikariConfig" class="com.zaxxer.hikari.HikariConfig">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/sample" />
        <property name="username" value="root"/>
        <property name="password" value="비밀번호"/>
        <property name="dataSourceProperties">
            <props>
                <prop key="cachePrepStmts">true</prop>
                <prop key="prepStmtCacheSize">250</prop>
                <prop key="prepStmtCacheSqlLimit">2048</prop>
            </props>
        </property>
    </bean>

    <bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
        <constructor-arg ref="hikariConfig" />
    </bean>

 

  • HikariCP를 사용하기 위해서는 HikariConfig 객체와 HikariDataSource를 초기화해야함
  • root-context.xml을 이용해서 HikariConfig 객체와 HikariDataSource 객체를 설정
  • hikariConfig에는 id 속성이 적용되어 있고 HikariDataSource는 <constructor-arg ref="hikariConfig" />로 id 값을 참조해서 사용
  • HikariDataSource는 javax.sql의 DataSource 인터페이스의 구현체이므로 테스트 코드를 통해서 설정에 문제가 없는지 확인

 

 

 

🎃  root-context.xml에 선언된 HikariCP를 주입받기 위해서 DataSource타입의 변수를 선언하고 @Autowired를 이용해서 주입 되도록 구성

 

 

 

 

 

 

 

 

 

 

 

 

 

 

👩🏻‍💻  스프링은 필요한 객체를 스프링에서 주입해 주기 때문에 개별적으로 클래스를 작성해서 빈으로 등록해 두기만 하면 원하는 곳에서 쉽게 다른 객체를 사용할 수 있음. 이런 특징으로 인해 스프링 프레임워크는 웹이나 데이터베이스 같은 특정한 영역이 아닌 전체 애플리케이션의 구조를 설계할 때 사용

 

 

 

 

[ 내용 참고 : IT 학원 강의 ]

+ Recent posts