기능 구현의 마지막이라고 할 수 있는 컨트롤러를 개발한다. UserController 클래스를 생성한다. 그리고 강의에서 컨트롤러에서는 직접 엔티티를 만들지 않고 DTO나 폼 형태로 따로 만들어서 사용하는게 좋다고 하셔서 나도 UserForm 클래스를 따로 만들었다. 유저 컨트롤러에서는 컴포넌트 스캔으로 스프링 빈에 등록하도록 @Controller와 생성자 주입을 자동으로 해주는 @RequiredArgConstructor를 선언해주고 로깅을 위해서 @Slf4j도 선언해주었다.
회원가입부터 만들기 시작했다. 먼저 Get방식으로 받은 다음에 model에 UserForm 객체를 넣어준 후 웹에 넘겨줬다. 그러면 thymeleaf를 이용해 값을 입력받도록 했다. 먼저 처음에 thymeleaf를 사용할 것이라고 알려준다.
<html xmlns:th="http://www.thymeleaf.org">
그 후 form 태그 안에 th:object를 사용해서 어떤 객체?에 받을 지 선언해주었다.
<form role="form" action="/user/register" th:object="${userForm}"
method="POST">
그 후 Input 태그에서 값을 컬럼명으로 받도록 구성해주면 된다.
<input type="text" th:field="*{loginId}" class="form-control"
th:field에서 id와 name 모두 설정해주기 때문에 따로 설정해주지 않아도 된다.
웹에서 사용자의 입력값을 받아서 빌더패턴으로 데이터를 만든 후 회원가입 메서드를 이용해 회원가입을 해주도록 만들었다. 그런데 기획 단계에서 선생님과 학생들을 구분하도록 만들기로 했기 때문에 구분하는 체크박스를 HTML에 구현해주었다. 그리고 체크박스를 체크했는지, 안했는지를 확인하도록 만들면 되는데 ... 이게 참 힘들었다. 구글링을 해서 어떻게 하는지는 알았는데 이게 적용이 안 됐다.
<input type="checkbox" id="rank" th:field="*{rank}" class="form-check-rank">
타임리프를 사용하지 않으면 데이터가 False가 아닌 null값으로 들어가는데, 타임리프에서 그것을 히든필드를 하나 더 생성해줘서 False로 받을 수 있게 해준다. 그래서 위의 코드에서 보이듯이 rank라는 컬럼을 하나 더 UserForm에 만들어줬다.
private Boolean rank;
이렇게 되면 rank에 true나 false가 들어와야하는데 .. 안 들어오고 계속 Null값으로 들어와서 이게 무슨 문제지 하고 계속 구글링 하고 .. 나온 내용 적용해보고 했는데 몇시간을 그렇게 헤매다가 @ModelAttribute 어노테이션을 파라미터값 옆에 작성해주면 된다는 내용을 봐서 적용해봤는데 다른 오류가 떠서 조금 갈피가 잡히기 시작했다. 그때 나온 오류가 java.io.EOFException: null 이런 오류였다. 찾아보니 @ModelAttribute는 Form에 @Setter를 선언해줘야 오류가 생기지 않는다고 한다. 그래서 마음에는 안 들었지만 선언만 해주고 사용을 하지 않으면 되는게 아닌가 싶어서 선언만 해주었더니 해결이 되었다.
그렇게 회원가입 기능까지는 완성이 되었는데, 에러메시지를 웹에 띄워야하는데 .. 이게 또 다시 문제였다. 그나마 강의에서 Validation 이라는 키워드를 알려줬기 때문에 이것을 찾아봤는데 뭐 여러가지 뜨긴 하는데 그것들은 모두 콘솔창에 띄우는 것들이었고 웹 단에 띄우는 것들은 아니었다. 그렇게 또다시 몇시간을 찾아보니 한가지 방법을 찾았다.
먼저 application.properties에
spring.messages.basename=messages,errors
이 구문을 추가해주고 resources에 errors.properties라는 파일을 하나 더 만들어준 후 (오류코드)=(전달할메시지)를 작성해준다.
required=필수값입니다.
validLogin=아이디나 비밀번호가 일치하지 않습니다
sameId=이미 있는 아이디입니다
그렇게 한 후 유저 컨트롤러에서 파라미터로 받을 값 왼쪽에 @Valid를 선언해주고 오른쪽에 BindingResult라는 타입으로 변수를 하나 만들어준다. 그 후 회원가입 기능이니 중복체크를 하는 코드가 있어야한다. 회원가입에서 진행하긴 하지만 그 전에 먼저 확인하고 리턴할 수 있도록 처음에 중복체크를 하고 만약 값이 이미 있을 경우
BindingResult.rejectValue("컬럼명","오류코드"); 를 해준다. 그 후
if (result.hasErrors()) {
return "/user/Register";
}
이렇게 추가해준다. 그러면 HTML쪽으로 에러코드가 날아가는데, 이걸 웹에서도 처리를 해주어야 한다. 이를 쉽게 해주는 것이 타임리프다.
<input type="text" th:field="*{loginId}" class="form-control"
th:class="${#fields.hasErrors('loginId')}? 'form-control fieldError' : 'form-control'"
placeholder="아이디를 입력하세요">
첫째 줄은 동일하나 둘째줄부터 에러를 처리해주는 코드이다.
@PostMapping("/user/register") // 회원가입 기능
public String register(@Valid @ModelAttribute UserForm form, BindingResult result) {
if(!userService.doublecheckLoginId(form.getLoginId()).isEmpty()){
result.rejectValue("loginId", "sameId");
}
if(form.getLoginId().isBlank() || form.getLoginPw().isBlank() || form.getName().isBlank()){
result.rejectValue("loginId", "required");
}
if (result.hasErrors()) {
return "/user/Register";
}
SchoolCategory schoolCategory;
if (form.getRank()){
schoolCategory = SchoolCategory.STUDENT;
} else {
schoolCategory = SchoolCategory.TEACHER;
}
User user = User.builder()
.name(form.getName())
.loginId(form.getLoginId())
.loginPw(userRepository.encryption(form.getLoginPw()))
.schoolCategory(schoolCategory)
.regDate(LocalDateTime.now())
.build();
userService.register(user);
return "redirect:/";
}
이렇게 회원가입 기능을 완성했다. 로그인 부분도 비슷한 부분이 많아 생략하겠다.
후기
Validation으로 에러 코드를 띄우는 작업이 진짜 어떻게 해야할지 감도 안 잡혀서 엄청 오래 걸렸던 작업인데 그만큼 해결했을 때 얼마나 기분이 좋았는지 모르겠다. 다음 일지를 마지막으로 현재까지 완성한 기능을 모두 기록할 수 있을 것 같다. 그 이후로는 하루에 한번씩 개발일지를 작성할 생각이다. 이렇게 미뤘다가 쓰려니 어떤 에러가 나왔었는지 기억이 잘 안나서 .. ㅎㅎ
'개발일지_development diary > YSit' 카테고리의 다른 글
YSit [9] - 유저 데이터 수정하기, 로그아웃하기 ( 쿠키 사용 ) (0) | 2022.12.27 |
---|---|
YSit [8] - UserController 개발하기 2 (0) | 2022.12.24 |
YSit [6] - 유저 기능 테스트하기 (0) | 2022.12.24 |
YSit [5] - UserRepository, Service 개발하기 (0) | 2022.12.24 |
YSit [4] - 엔티티 개발하기 (0) | 2022.12.24 |