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 학원 강의 ]

+ Recent posts