본문 바로가기

신세계 국비과정/신세계 - Spring 공부

14주차 배운점 - Todo프로그램, ModelMapper, log4j2

728x90
반응형

 

 

Todo 프로그램 만들기

 

TodoService

 

modelMapper통해서 vo로 만들고 todoDAO에 insert 요청

 

public enum TodoService { //TodoService 는 DTO, VO 사용하는 구조이므로, ModelMapper와 TodoDAO 를 이용하도록 구성한다.
    INSTANCE;

    private TodoDAO todoDAO;
    private ModelMapper modelMapper;


    //
    TodoService(){
       todoDAO = new TodoDAO();
       modelMapper = MapperUtil.INSTANCE.get();
    }

    //register() 는 TodoDTO 를 파라미터로 받아서 TodoVO변환 기능
    public void register(TodoDTO todoDTO) throws Exception {
        TodoVO vo = modelMapper.map(todoDTO, TodoVO.class); // 클래스 변환
        System.out.println("todoVO" + vo);
        todoDAO.insert(vo);
    }

 

todoDao의 insert 에서 받는다.

 

 public void insert(TodoVO todoVO) throws Exception {
        String sql = "insert into tbl_todo (title,dueDate,finished) values (?,?,?)";

        @Cleanup Connection connection = ConnectionUtil.INSTANCE.getConnection();
        @Cleanup PreparedStatement psmt = connection.prepareStatement(sql);

        psmt.setString(1, todoVO.getTitle());
        psmt.setDate(2, Date.valueOf(todoVO.getDueDate()));
        psmt.setBoolean(3, todoVO.isFinished());

        psmt.executeUpdate();
    }

 

테스트 insert

 

public class TodoServiceTests {

    //1. TodoService 객체 선언
    private TodoService todoService;

    //2. @BeforeEach 통해서 ready를 메소드를 이용하여 TodoService 객체 생성
    @BeforeEach
    public void ready(){
        todoService = TodoService.INSTANCE;
    }

    //3. @Test : testRegister 메소드에 TodoDTO를 하나를 빌더를 통해서 (TITLE, Duedate)를 생성한 후 서비스 등록을 수행한다.
    @Test
    public void testRegister() throws Exception {
        TodoDTO dto = TodoDTO.builder().tno(8l).title("테테스스트트").dueDate(LocalDate.of(2023,02,23)).finished(true).build();
        todoService.register(dto);
    }

    //4. testRegister() 실행한 후 정상적으로 TodoVO의 내용이 출력되는 지 확인

    //5. tbl_todo테이블에 insert가 정상적으로 입력되었는지 확인

}

 

(service)dto 객체를 만들어서 -> vo로 변환하고 -> (dao)vo를 받아서 db에 insert해줌

 

"ModelMapper"는 Java에서 사용되는 객체 매핑 라이브러리. 이 라이브러리는 객체 간의 매핑을 자동화한다. 주로 DTO(Data Transfer Object)와 Entity(데이터베이스에서 가져온 객체) 간의 변환을 담당

 

 

 

ModelMapper

 

public enum  { // 싱글톤
    INSTANCE;

    private ModelMapper modelMapper;
    //object mapping 서로 다른 클래스의 값을 한번에 복사하게 도와주는 라이브러리1

    MapperUtil(){
        this.modelMapper = new ModelMapper();
        this.modelMapper.getConfiguration()
                .setFieldMatchingEnabled(true)
                .setFieldAccessLevel(Configuration.AccessLevel.PRIVATE)
                .setMatchingStrategy(MatchingStrategies.STRICT);

    }

    public ModelMapper get(){
        return modelMapper;
    }
}

 

// setFieldMatchingEnabled(true): 이 설정은 ModelMapper가 객체 간 매핑을 할 때 필드
// 이름이 같은 필드들끼리 매핑을 수행하도록 합니다. 즉, 게터(getter)와 세터(setter) 메소드를 사용하지 않고
// 직접 필드에 접근하여 값을 할당합니다.
//
// setFieldAccessLevel(Configuration.AccessLevel.PRIVATE): 이 설정은 ModelMapper가
// private 접근 제한자를 가진 필드에도 접근할 수 있도록 허용합니다.기본적으로 ModelMapper는
// public 또는 protected 필드만 접근할 수 있지만, 이 설정을 통해 private 필드에도
// 접근이 가능해집니다.
//
// setMatchingStrategy(MatchingStrategies.STRICT): 매핑 전략을 설정하는 부분으로,
// STRICT 전략을 사용하면 매핑 과정에서 소스와 목적지 객체 간의 필드 이름이 정확히 일치해야 합니다.
// 이는 느슨한 매핑(LOOSE 전략)과 대조되는데, 느슨한 매핑은 이름이 유사하기만 해도 매핑을 수행합니다.
// STRICT 전략을 사용하면 데이터의 정확한 매핑이 보장됩니다.
//
// 예를 들어, Source 객체에 firstName이라는 필드가 있고, Destination 객체에도 firstName이라는
// 필드가 있을 때, STRICT 매칭 전략을 사용하면 ModelMapper는 이 두 필드를 매핑합니다.
// 만약 필드 이름이 약간 다르면 (예: first_name과 firstName), STRICT 전략에서는 매핑하지 않을 것입니다.

 

 

하나의 서비스객체에 연동해서 , 여러 컨트롤러로, 등록, 수정, 삭제 .. 묶어서 하나의 서비스 구현

 

 

TodoRegisterController

 

 

localhost/todo/register 로 들어가면 TodoRegisterController의 doGet메소드가 실행된다. register.jsp페이지로 이동한다.

 

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("/WEB-INF/todo/register.jsp").forward(request, response);
    }

 

register.jsp

 

입력하는 title, 날짜선택하는 input이 존재하고 submit 버튼을 누르면 action="/todo/register" register 컨트롤러가 요청을 받는다.

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<html>
<head>
    <title>Title</title>
</head>
<body>

<form action="/todo/register" method="post" accept-charset="UTF-8">
    <input type="text" id="title" name="title">
    <input type="date" id="dueDate" name="dueDate">
    <button type="submit" name="RGISTER">등록</button>
</form>

</form>
</body>
</html>

 

 

 

TodoRegisterController

 

register 컨트롤의 doPost가 실행된다. 요청받은 title, dueDate를 사용하여 todoDTO 객체를 만들어 todoService.register 메소드로 디비에 추가한다.


그 후 sendRedirect로 list 컨트롤러로 이동한다.

 

 

@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");

        String title = request.getParameter("title");
        String dueDate = request.getParameter("dueDate");
        System.out.println(title);
        System.out.println(dueDate);

        TodoDTO todoDTO = TodoDTO.builder()
                .title(title)
                .dueDate(LocalDate.parse(dueDate))
                .finished(true)
                .build();

        try {
            todoService.register(todoDTO);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        response.sendRedirect("/todo/list");
    }

 

 

todoService , 모든 리스트 가져오기 기능

 


dao에서 근데 vo 니까 변환 과정 필요함


dao에 있는 selectAll한테 보냄

 

public List<TodoDTO> listAll() throws Exception{
        List<TodoVO> voList = todoDAO.selectAll();
        // 반환이 VO니까 일단 VO로 받음
        List<TodoDTO> dtoList = voList.stream().map(vo->modelMapper.map(vo,TodoDTO.class)).collect(Collectors.toList());
        //한개씩 dto로 바꿈
        return dtoList;
    }

 

controller


/todo/list로 접속해서 doGet메소드가 실행된다.


List<TodoDTO>리스트를 만들고 request에 setAttribute한다. 그리고 dispather 로 보낸다.

 

 

@WebServlet(name="todoListController",urlPatterns = "/todo/list")
public class TodoListController extends HttpServlet {
    //1. todoService 객체
    private TodoService todoService = TodoService.INSTANCE; // enum 클래스의 싱글톤 패턴을 만들었기 때문에
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{
        System.out.println("todo list ... called");

        try {
            List<TodoDTO> dtoList = todoService.listAll();
            request.setAttribute("dtoList",dtoList); // request 뭔가 담을 수 있는 저장공간
            request.getRequestDispatcher("/WEB-INF/todo/list.jsp").forward(request,response);
        } catch (Exception e) {
            throw new RuntimeException("list error");
        }

    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{

    }
}

 

 

todo/list.jsp


내용 출력됨
setAttribute "dtoList"이름으로 보냈다. 리스트를 가리킨다.


하나씩 돌면서 출력됨

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
 //--컨테이너가 해석하는 서버스크립트 태그 
 
<html>
<head>
    <title>TodoList</title>
</head>
<%--el사용하면 getter setter 같은 복잡한 과정 필요없음--%>
<body>
<form>
    <c:forEach items="${dtoList}" var="dto">
        <a href="/todo/read?param=${dto.tno}">${dto}</a><br>
    </c:forEach>
</form>
</body>
</html>

 

리스트에 a태그를 달아 놓았다. 하나 선택해준다.


요구사항이 get방식으로 read 페이지에 전달해야하므로 href="/todo/read?param=${dto.tno} 하여 선택한 게시물 번호를 전달한다.

 

 

 

TodoReadController

 

list.jpg 에서 get방식으로 url에 선택한 todo 번호를 전달했다.
String tno = request.getParameter("param"); 에서 param 이라는 키 값으로 번호를 받아 tno에 담는다.
todoService.selectOne(Long.parseLong(tno))로 tno를 보내주어 TodoDTO객체 하나를 전달 받는다.
선택한 todo를 보여주기 위해 request.setAttribute 에다 Tno, Title, DueDate값들을 붙여주고 read.jsp로 보낸다.

 

 

 @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String tno = request.getParameter("param");
        try {
            TodoDTO dto = todoService.selectOne(Long.parseLong(tno));
            System.out.println(dto);
            request.setAttribute("dto",dto.getTno());
            request.setAttribute("title",dto.getTitle());
            request.setAttribute("dueDate",dto.getDueDate());

            request.getRequestDispatcher("/WEB-INF/todo/read.jsp").forward(request, response);

        } catch (Exception e) {
            throw new RuntimeException(e);
        }


    }

 

read.jsp

 

 

컨트롤러에서 보낸 값들을 <label>을 통해 출력해 주었다.
수정,삭제 하기위한 <a>을 놨뒀다. 수정이나 삭제를 할때 사용하기 위해 누르면 modify 컨트롤러에게 param=%{dto} 를 넘겨 준다.

 

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c"%>
<html>
<head>
    <title>Read</title>
</head>
<body>
    <label>READ</label>

    <form action="todo/read" method="post">

        <label>${dto}</label><hr>
        <label>${title}</label><hr>
        <label>${dueDate}</label><hr>

        <a href="/todo/modify?param=${dto}">Modify/Remove</a><br>
    </form>


</body>
</html>

 

 

 

TodoModifyController

 

 

get 방식으로 보냈으므로 modify 컨트롤러의 doGet이 실행된다.
param 키값을 가지는 파라미터를 tno에 저장했다.
그 후 tno 값을 가진채로 modify.jsp로 이동한다.

 

@Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String tno = request.getParameter("param");
        System.out.println("update tno : "+tno);

        request.setAttribute("tno",tno);

        request.getRequestDispatcher("/WEB-INF/todo/modify.jsp").forward(request, response);

    }

 

TodoModify.jsp

 

 

컨트롤러에서 보낸 tno 를 hidden <input>태그에 놨뒀다.
그래서 submit 할 때 사용자는 눈에 안보이지만 tno값을 전달할 수 있다.
Modify 버튼과, remove 버튼을 두어 tno가 선택된채로 수정 또는 삭제를 할 수 있다.
${tno} 는 내가 선택한 todo 이므로 따로 변경없이 그냥 박아 놨다.
예시는 4번을 선택해서 4번이 나온다.
삭제하고 싶으면 remove 버튼을 누르는데 input에 value속성으로 ${tno}를 넣어줘서 뭘 선택했는지 알 수 있다.

 

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>TodoModify</title>
</head>
<body>
    <h1>modify</h1>

<form action="/todo/modify" method="post">
    <input type="hidden" name=tno value="${tno}"><br>
    ${tno}<br>
    <input type="text" name="title"><br>
    <input type="date" name="dueDate"><br>
    <button type="submit">Modify</button>
</form>
<form action="/todo/remove" method="post">
    <input type="hidden" name=tno value="${tno}"><br>
    <button type="submit">remove</button>
</form>


</body>
</html>

 

 

 

 

TodoModifyController

 

 

modify 버튼을 누르게 되면 TodoModify컨트롤러의 doPost가 실행된다.
tno, titl, dueDate를 받는다.


받은 데이터를 통해서 dto 를 새로 만들고 todoService.updateOne 으로 보낸다. 해당 내용으로 변경을 한 이후에 sendRedirect("/todo/list")로 이동

 

@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        System.out.println("post page access");

        String tno = request.getParameter("tno");
        String title = request.getParameter("title");
        String dueDate = request.getParameter("dueDate");

        System.out.println(tno);
        System.out.println(title);
        System.out.println(dueDate);

        TodoDTO dto = TodoDTO.builder()
                .tno(Long.parseLong(tno))
                .title(title)
                .dueDate(LocalDate.parse(dueDate))
                .build();

        try {
            todoService.updateOne(dto);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        response.sendRedirect("/todo/list");
    }

 

 

TodoModify.jsp 에서 remove를 누르면 TodoRemoveController 의 doPost가 실행된다.


<input type="hidden" name=tno value="${tno}"> 를 통해 tno를 받았기 때문에 request.getParameter로 받을 수 있다.
해당 tno로 todoService.deleteOne를 진행하여 삭제한다.


그 후 /todo/lis 페이지로 sendRedirect한다.

 

 

@Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String tno = request.getParameter("tno");
        System.out.println("remove page  "+Long.parseLong(tno));

        try {
            todoService.deleteOne(Long.parseLong(tno));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        response.sendRedirect("/todo/list");
    }

 

 

 


 

 

 

로그 기능: log4j2 라이브러리

 

 

level(레벨) 설정


Lombok의 경우 @Log4j2 를 이용하여 소스 코드 내에 로그를 적용할 수 있다.

  • level (INFO를 많이함)
    로그의 레벨이 높아질 수록
    내가 지정한 레벨이상의 로그들만 나옴 , 개발할 때는 낮춰놓음 다 볼 수 있게
  • Appender

 

FATAL: 가장 심각한 오류를 나타내며, 애플리케이션이 계속 실행될 수 없는 상태입니다.
ERROR: 예상치 못한 상황에서 발생한 오류로, 애플리케이션의 일부 기능이 제대로 작동하지 않을 때 사용됩니다.
WARN: 잠재적으로 해로운 상황을 경고할 때 사용됩니다. 오류는 아니지만, 주의가 필요한 상황입니다.
INFO: 애플리케이션의 일반적인 이벤트를 기록할 때 사용됩니다. 사용자에게 유용한 정보나 상태 업데이트 등에 사용됩니다.
DEBUG: 개발 과정에서 상세한 실행 정보를 기록할 때 사용됩니다. 문제 해결 및 애플리케이션의 흐름을 추적하는 데 도움이 됩니다.
TRACE: DEBUG보다 더 상세한 정보를 기록할 때 사용됩니다. 애플리케이션의 거의 모든 실행 경로를 추적할 수 있으며, 문제 해결에 매우 유용합니다.

 

 

build.gradle 추가

 

implementation group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.17.2'
    implementation group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.17.2'
    implementation group: 'org.apache.logging.log4j', name: 'log4j-slf4j-impl', version: '2.17.2'

 

 

 

log4j2.xml 작성

 

<?xml version="1.0" encoding="utf-8" ?>
<Configuration status="WARN" xmlns="http://logging.apache.org/log4j/2.0/config">
<!--    info 3단계 warn이 4단계-->
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </Console>
    </Appenders>
<!--    이 로그가 어디에 보여질 것인지 지정-->
    <Loggers>
        <Root level="info">
            <AppenderRef ref="Console"/>
        </Root>
    </Loggers>
</Configuration>

 

TodoService 위에 어노테이션 추가, 메서드에 log.info(vo)

 

@Log4j2 // 어노테이션
public enum TodoService { //TodoService 는 DTO, VO 사용하는 구조이므로, ModelMapper와 TodoDAO 를 이용하도록 구성한다.
    INSTANCE;

    private TodoDAO todoDAO;
    private ModelMapper modelMapper;


    
    TodoService(){
       todoDAO = new TodoDAO();
       modelMapper = MapperUtil.INSTANCE.get();
    }

    //register() 는 TodoDTO 를 파라미터로 받아서 TodoVO변환 기능
    public void register(TodoDTO todoDTO) throws Exception {
        TodoVO vo = modelMapper.map(todoDTO, TodoVO.class); // 클래스 변환
//        System.out.println("todoVO" + vo);
        log.info(vo);  // 보고싶은곳 메서드 추가 
        todoDAO.insert(vo);
    }

 

좀 다른 에러 나옴

 

 

Test에서도 사용가능

 

@Log4j2
public class TodoServiceTests {
반응형