꾸준하게 기록하기
article thumbnail

'[책] 스프링부트 시작하기 (김인우)'를 공부하면서 적은 내용입니다.


DTO(Data Transfer Object) 만들기

DTO는 어플리케이션 내의 각 계층 간 데이터를 주고받는 데 사용하는 객체이다.

계층이란 뷰, 컨트롤러, DAO, DB 등을 의미한다.

 

board 패키지 아래 dto 패키지 생성하고 BoardDto 클래스 생성한다.

package com.insight.board.board.dto;

import lombok.Data;

@Data  // 롬복의 어노테이션으로 모든 필드의 getter,setter 를 생성
public class BoardDto {
	// 데이터 베이스의 게시판 테이블 칼럼과 매칭
    private int boardIdx;
    private String title;
    private String contents;
    private int hitCnt;
    private String creatorId;
    private String createdDatetime;
    private String updaterId;
    private String updatedDatetime;
}

 

마이바티스 설정하기

1. application.properties 설정 추가

mybatis.configuration.map-underscore-to-camel-case=true

myBatis가 제공하는 설정 중 mapUnderscoreToCamel은 전통적인 데이터베이스의 컬럼과 자바 변수를 매핑해주는 기능을 합니다. 기본적으로 false로 설정되어 있으며, 이를 true로 변경하면 됩니다.

 

2. baen 등록

앞서 설정한 mapUnderscoreToCamelCase 설정값을 적용해야 합니다.

 

1) DatabaseConfiguration 클래스 내에 다음 코드를 추가합니다.

@Bean
@ConfigurationProperties(prefix="mybatis.configuration") // 1
public org.apache.ibatis.session.Configuration mybatisConfig() {
    return new org.apache.ibatis.session.Configuration();  // 2
}

1. application.properties 의 설정 중 마이바티스에 관련된 설정을 가져온다.

2. 가져온 마이바티스 설정을 자바 클래스로 만들어서 반환한다.

 

 

2) sqlSessionFactory 메서드에 추가 

@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(dataSource);
    sqlSessionFactoryBean.setMapperLocations(applicationContext.getResources("classpath:/mapper/**/*.xml")); 
    // 해당 코드 한줄만 추가 
    sqlSessionFactoryBean.setConfiguration(mybatisConfig());
    return sqlSessionFactoryBean.getObject();
}

 

컨트롤러

클라이언트의 요청을 받아서 해당 요청을 수행하는데 필요한 비즈니스 로직 호출 그 결과를 포함하여 응답해주는 디스패처(Dispatcher) 역할을 한다.

 

1. 컨트롤러 클래스에 @Controller 어노테이션을 적용

2. @RequestMapping 어노테이션을 이용해 요청에 대한 주소를 지정

3. 요청에 필요한 비즈니스 로직 호출

4. 실행된 비즈니스 로직의 결과를 뷰로 리턴

 

board 패키지 밑에 board.controller 패키지를 생성한 후, controller 패키지 안에 BoardController 클래스를 생성

package com.insight.board.board.controller;

import com.insight.board.dto.BoardDto;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.servlet.ModelAndView;

import java.util.List;

@Controller
public class BoardController {
    @Autowired
    private BoardService boardService; //서비스와 연결
    
    @RequestMapping("/board/openBoardList.do")
    public ModelAndView openBoardList() throws Exception{
        ModelAndView mv = new ModelAndView("/board/boardList");
        List<BoardDto> list = boardService.selectBoardList();
        mv.addObject("list", list);
        return mvc;
    }
}

@Controller : 해당 클래스를 컨트롤러로 동작하게 한다.

@RequestMapping : 클라이언트에서 호출한 주소와  @RequestMapping 값이 동일한 메소드 찾아서 실행

ModelAndView mv = new ModelAndView("/board/boardList"); 

: 호출된 요청의 결과를 보여 줄 뷰를 지정. templates 폴더 아래의 board/boardList.html 파일을 의미한다.

List<BoardDto> list = boardService.selectBoardList();

: 게시글 목록을 조회하기 위해 BoardService클래스의 selectBoardList 메서드 호출

mv.addObject("list", list);

: 실행된 비즈니스 로직의 결과 값을 뷰에 list 라는 이름으로 저장한다.

 

서비스 영역

Service 인터페이스와 ServiceImpl 클래스로 구성된다. 

 

분리하는 경우 장점

1. 느슨한 결함을 유지하여 각 기능 간의 의존관계를 최소화한다.

2. 의존관계의 최소화로 인해 기능의 변화에도 최소한의 수정으로 개발할 수 있는 유연함 가질 수 있다.

3. 모듈화를 통해 어디서든 사용할 수 있도록 하여 재사용성 높인다.

4. 스프링의 IoC/DI(Inversion of Control / Dependency Injection) 기능을 이용한 빈 관리 기능 사용

 

위와 같은 장점이 있더라도 개발하는 시스템 환경에 따라 굳이 나눌 필요가 없을 수도 있다.

 

board 패키지 밑에 service 패키지를 생성하고 BoardService 인터페이스와 BoardServiceImpl 클래스를 생성

BoardService.java

package com.insight.board.service;

import com.insight.board.dto.BoardDto;
import java.util.List;

public interface BoardService {
    List<BoardDto> selectBoardList() throws Exception;
}

BoardServiceImpl.java

package com.insight.board.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import board.board.dto.BoardDto;
import board.board.mapper.BoardMapper;

@Service
public class BoardServiceImpl implements BoardService {

    @Autowired
    private BoardMapper boardMapper;

    @Override
    public List<BoardDto> selectBoardList() throws Exception {
        return boardMapper.selectBoardList();
    }
}

BoardService 인터페이스에는 비즈니스 로직을 수행하기 위한 메소드를 정의

BoardServiceImpl 클래스는 BoardService 인터페이스를 사용하여 실제 기능을 구현

 

@Service : 비즈니스 로직을 처리하는 서비스 클래스를 나타내는 어노테이션. 스프링 MVC의 서비스임을 나타낸다.

private BoardMapper boardMapper; → 데이터베이스에 접근하는 DAO 빈을 선언

return boardMapper.selectBoardList();  사용자 요청을 처리하기 위한 비즈니스 로직을 구현

 

매퍼 영역

마이바티스에서는 DAO보다 SqlSessionDaoSupport나 SqlSessionTemplate를 사용하기를 권장

매퍼를 사용하면 일일이 DAO를 만들지 않고, 인터페이스만을 이용해 편하게 개발이 가능하다.

 

board 패키지 아래 mapper 패키지 생성 BoardMapper 인터페이스 생성

BoardMapper.java

package com.insight.board.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Mapper;

import board.dto.BoardDto;

@Mapper		// Mapper라고 선언함
public interface BoardMapper {
	// 여기서 지정한 메서드의 이름은 쿼리의 이름과 동일해야 함 (selectBoardList)
	List<BoardDto> selectBoardList() throws Exception; 
}

 

SQL 작성하기

마이바티스는 쿼리를 XML에 작성하고 아이디를 이용하여 매핑한다.

XML 파일의 경우 src/main/java 폴더가 아니라 src/main/resources 에 넣는다.

 

src/main/resources/mapper 에 sql-board.xml 생성

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.insight.board.mapper.BoardMapper"> <!-- xml파일을 사용할 mapper가 있는 경로 -->
    <select id="selectBoardList" resultType="com.insight.board.dto.BoardDto">
        <![CDATA[
            SELECT
                board_idx,
                title,
                hit_cnt,
                created_datetime
            FROM
                t_board
            WHERE
                deleted_yn = 'N'
            ORDER BY board_idx DESC
        ]]>
    </select>
</mapper>

 

<select> 태그를 이용하여 select 쿼리임을 나타낸다.

mapper에서 말했던 메소드의 이름은 select 쿼리의 id값과 같아야함.

resultType은 이 쿼리 실행 결과가 앞서 만든 DTO인 BoardDto 형식으로 반환되는 것을 의미합니다

parameterType이라는 속성은 입력되는 파라미터의 형식을 지정해준다.

 

parameterType과 resultType을 명시할 때는 해당 클래스의 패키지를 포함해서 지정해야한다.

 

뷰 작성하기

src/main/resources/templates 폴더 밑에 board 폴더 생성하고 boardList.html 파일 생성

<!DOCTYPE html>
<html lang="ko" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>board</title>
    <link rel="stylesheet" th:href="@{/css/style.css}"/>
</head>
<body>
    <div class="container">
        <h2>게시글 목록</h2>
        <table class="board_list">
            <colgroup>
                <col width="15%"/>
                <col width="*"/>
                <col width="15%"/>
                <col width="20%"/>
            </colgroup>
            <thead>
                <tr>
                    <th scope="col">글번호</th>
                    <th scope="col">제목</th>
                    <th scope="col">조회수</th>
                    <th scope="col">작성일</th>
                </tr>
            </thead>
            <tbody>
                <tr th:if="${#lists.size(list)} > 0" th:each="list : ${list}">
                    <td th:text="${list.boardIdx}"></td>
                    <td class="title" th:text="${list.title}"></td>
                    <td th:text="${#temporals.format(list.createdDatetime, 
                        'yyyy-MM-dd HH:mm::ss')}"></td>
                </tr> 
                <tr th:unless="${#lists.size(list)} > 0">
                    <td colspan="4">조회된 결과가 없습니다.</td>
                </tr>
            </tbody>
        </table>
        <a href="/board/openBoardWrite.do" class="btn">글 쓰기</a>
    </div>
</body>
</html>

xmlns:th="http://www.thymeleaf.org">

→ Thymeleaf의 th 속성을 사용하기 위한 네임스페이스를 선언

<tr th:if="${#lists.size(list)} > 0" th:each="list : ${list}"> 

 조회된 데이터가 한 개 이상인 경우 목록으로 보여줌

<tr th:unless="${$lists.size(list)} > 0">

→ 데이터가 없을 경우 "조회된 결과가 없다"는 메세지를 보여준다.

<td class="title" th:text="${list.title}"></td>

list 변수를 통해 서버에서 전달된 데이터에 접근해서 화면에 표시합니다. 

쿼리에서 게시글에 필요한 board_idx, title, hit_cnt, created_datetime이라는 컬럼을 조회하고,

이는 BoardDto 클래스의 boardIdx, title, hitCnt, createdDatetime이라는 변수에 저장되어 있습니다. 

뷰에서는 서버로부터 전달된 게시글 목록을 list 변수를 통해 접근할 수 있습니다.

 

실행

localhost:8080/board/openBoardList

 

 

INSERT INTO t_board SET title="게시글1", contents="내용1", created_datetime=NOW(), creator_id="hong";

게시글 등록해 준 후 다시 실행해보면 

 

 

728x90
profile

꾸준하게 기록하기

@:_:

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!