이전 개발 단계 보러 가기
https://taetae99.tistory.com/31
벡엔드 토이 프로젝트(게시판) - 로그인 및 게시판 메인화면 구현, Spring Security를 사용한 로그인 (
이전 개발 단계 보러 가기 https://taetae99.tistory.com/30 벡엔드 토이 프로젝트 (게시판) - 게시글,댓글 기능 개발 및 테스트 (3)이전 개발 단계 보러 가기https://taetae99.tistory.com/28 벡엔드 토이 프로젝
taetae99.tistory.com
목차
0. 이번 포스팅의 주요 목표
세 가지에 신경을 쓰며 코드를 수정 및 작성하였다.
1. 컨트롤러에서 엔티티를 직접 생성하거나 리포지토리를 직접 호출하는 대신, 최대한 서비스 계층에서 처리하며 트랜잭션 안에서 비즈니스 로직을 수행한다.
2. 데이터를 삽입하거나 변경하는 경우 의미 있는 메서드를 엔티티 안에 작성하여 유지보수가 쉽도록 한다.
3. Member와 관련된 데이터를 처리할 땐, 직접 Member 엔티티를 사용하는 것이 아닌 Member와 관련된 DTO나 FORM을 생성하여 데이터를 주고받는다.
Member 데이터를 사용하는 경우는 크게 세 가지이다.
가입 시 : 이름, 비밀번호, 이메일, 닉네임를 필요로 한다.
로그인 시 : 이메일, 비밀번호를 필요로 한다.
마이 페이지 : 이름, 이메일, 가입일, 닉네임, 작성한 게시글, 작성한 댓글을 필요로 한다.
이때 각각의 경우에 필요한 데이터가 다르기 때문에 총 세 가지의 DTO 및 FORM을 생성을 할 것이다.
이때 Form을 사용하는 경우와 Dto를 사용하는 경우의 차이점이 존재하는가에 대한 의문이 생겨 검색을 하였다.
이에 대한 답변은 아래와 같다.
Form이나 DTO 모두 단순히 계층간에 데이터를 전달할 때 사용한다. 그 둘의 역할은 동일하다.
하지만 Form이라는 것은 제약을 더 두어서 명확하게 컨트롤러까지만 사용해야 한다는 의미를 두었다.
Dto는 Data Transfer Object로 데이터 전송 객체라고 부른다. 이는 더 범용적으로 사용되는 단어이다.
- 김영한 강사님 -
위의 답변을 토대로 세 가지의 경우에 맞는 Dto 및 Form을 생성한다.
가입 시에는 뷰와 컨트롤러 사이의 데이터를 전송하므로 Form을 사용한다. -> MemberForm
로그인 시에는 뷰와 컨트롤러 사이의 데이터를 전송하므로 Form을 사용한다. -> LoginForm
마이페이지에는 서비스단에서 데이터를 종합하여 컨트롤러로 전달하므로 Dto를 사용한다. -> MemberInfoDto
1. MemberForm (회원 가입 데이터)
@Getter
public class MemberForm {
//null 이거나 빈 값일 경우 유효하지 않다.
@NotEmpty(message = "{member.name.empty}")
@Size(min = 2,max = 15,message = "{member.name.size}")
private String name;
@NotEmpty(message = "{member.password.empty}")
@Size(min=4,max = 12,message = "{member.password.size}")
@Pattern(regexp = "^[a-zA-Z0-9]*$", message = "{member.password.format}")
private String password;
@NotEmpty(message = "{member.email.empty}")
@Email(message = "{member.email.format}")
private String email;
@NotEmpty(message = "{member.nickname.empty}")
private String nickname;
public MemberForm(String name, String password, String email, String nickname) {
this.name = name;
this.password = password;
this.email = email;
this.nickname = nickname;
}
}
유효성 검사를 위하여 다양한 어노테이션을 사용하였다.
이는 아래에서 설명하겠다.
2. LoginForm (로그인 데이터)
@Getter
public class LoginForm {
private String email;
private String password;
}
3. MemberInfoDto (마이페이지 데이터)
@Getter
public class MemberInfoDto {
private String name;
private String email;
private LocalDateTime createdDate;
private String nickname;
private List<Post> posts;
private List<Comments> comments;
protected MemberInfoDto() {
}
private MemberInfoDto(String name, String email, LocalDateTime createdDate,
String nickname, List<Post> posts,List<Comments> comments) {
this.name = name;
this.email = email;
this.createdDate = createdDate;
this.nickname = nickname;
this.posts = posts;
this.comments = comments;
}
public static MemberInfoDto createMemberInfo(String name, String email, LocalDateTime createdDate,
String nickname,List<Post> posts,List<Comments> comments){
return new MemberInfoDto(name, email, createdDate, nickname, posts, comments);
}
}
1. 마이페이지 구현하기
1. 마이페이지 UI

마이페이지 버튼을 누르면 나오는 페이지의 모습이다.
내 정보에는 이름, 이메일, 가입일, 닉네임을 확인 할 수 있다.
작성한 게시글에는 본인이 작성한 게시글의 제목, 작성일, 조회수를 볼 수있다. 제목을 클릭하면 해당 게시글로 넘어간다.
작성한 댓글에는 본인이 작성한 댓글과 댓글을 작성한 게시글의 제목을 대괄호로 감싸져 보여준다.
댓글 또한 클릭하면 해당 게시글로 넘어가도록 구현한다.
2. MemberController의 마이페이지 조회 기능
@GetMapping("/auth/mypage")
public String myPageForm(@AuthenticationPrincipal MemberDetail memberDetail, Model model){
MemberInfoDto memberInfo = memberService.getMemberInfo(memberDetail.getUsername());
model.addAttribute("memberInfo",memberInfo);
return "member/myPageForm";
}
/auth/mypage로 GET요청이 들어오면 현재 로그인된 멤버의 정보(@AuthenticationPrincipal)에서 이메일 정보를 가져오고 MemberService를 통해 DB에서 멤버 정보를 가져온다.
3. MemberInfoDto를 생성하는 비즈니스 로직
@Transactional
public MemberInfoDto getMemberInfo(String email) {
Member findByEmail = memberRepository.findByEmail(email);
List<Post> findPosts = postRepository.findAllByMember(findByEmail.getId());
List<Comments> findComments = commentRepository.findByMember(findByEmail.getId());
return MemberInfoDto.createMemberInfo(findByEmail.getName(),
findByEmail.getEmail(),
findByEmail.getCreatedDate(),
findByEmail.getNickname(),
findPosts,
findComments);
}
이때 getMemberInfo에서는 이메일로 찾은 멤버 정보와 작성한 게시글, 댓글을 MemberInfoDto에 저장하여 반환한다.
💡 Member와 Post는 양방향 연관관계로 매핑하였기 때문에 findByEmail에서 바로 getPosts()를 사용하여 게시글을 가져올 수 있다. 이를 이용해서 나중에 성능 개선이 유효한지 확인해보고자 한다.
2. 회원 가입 유효성 검사
1. MemberForm
@Getter
public class MemberForm {
//null 이거나 빈 값일 경우 유효하지 않다.
@NotEmpty(message = "{member.name.empty}")
@Size(min = 2,max = 15,message = "{member.name.size}")
private String name;
@NotEmpty(message = "{member.password.empty}")
@Size(min=4,max = 12,message = "{member.password.size}")
@Pattern(regexp = "^[a-zA-Z0-9]*$", message = "{member.password.format}")
private String password;
@NotEmpty(message = "{member.email.empty}")
@Email(message = "{member.email.format}")
private String email;
@NotEmpty(message = "{member.nickname.empty}")
@Pattern(regexp = "^[a-zA-Z0-9가-힣]*$", message = "{member.nickname.format}")
@Size(min = 2,max = 8,message = "{member.nickname.size}")
private String nickname;
public MemberForm(String name, String password, String email, String nickname) {
this.name = name;
this.password = password;
this.email = email;
this.nickname = nickname;
}
}
@NotEmpty는 null이거나 빈 값일 경우에 대한 유효성 검사를 진행한다.
@Size는 해당 필드의 길이가 최솟값(min)과 최댓값(max) 사이에 있는지 확인한다.
@Email은 해당 필드가 이메일 형식에 맞는 지 검색한다.
@Pattern은 정규식을 토대로 해당 형식에 맞게 입력되었는지 검증한다.
->password는 영문 대소문자 및 숫자를 허용한다.
message속성을 사용하여 유효성 검사 실패 시 사용자에게 보여줄 메시지를 지정한다.
기존 요구사항에서는 비밀번호의 길이를 min=8, max=16으로 설정하였지만 테스트를 위해 잠시 변경하였다.
2. message 속성 관리

messages.properties 파일을 resource 밑에 생성한다.
이후 application.yml에 맞는 설정을 해야 한다. -> 간단하므로 설정 방법은 패스하겠다.
3. messages.properties
member.name.size = 이름은 {min}자 이상 {max}자 이하로 입력해야 합니다.
member.name.empty = 회원 이름은 필수 입니다.
member.password.size = 비밀번호는 {min}자 이상 {max}자 이하로 입력해야 합니다.
member.password.empty = 비밀번호는 필수 입니다.
member.password.format = 비밀번호는 영어와 숫자만 사용할 수 있습니다.
member.email.format = 이메일 형식이 잘못 입력되었습니다.
member.email.empty= 이메일은 필수 입니다.
member.nickname.empty = 닉네임은 필수 입니다.
member.nickname.format = 닉네임은 영어와 숫자, 한글만 사용할 수 있습니다.
member.nickname.size = 닉네임은 {min}자 이상 {max}자 이하로 입력해야 합니다.
이후 아까 지정했던 형식에 맞게 원하는 메시지를 작성한다.
4. 회원 가입 페이지 유효성 검사

회원 가입 시 위에서 설정한 유효성 검사가 실패하면 메시지와 함께 빨간색으로 경고 표시를 해준다.

또 다른 예시를 하나 보자. 이름과 관련된 유효성 검사는 두 개였다.
@NotEmpty와 길이에 대한 유효성 검사로 이전에는 이름과 관련되어 두 개의 오류가 발생했다.
하지만 위와 같이 @NotEmpty에 대한 조건을 만족한다면 길이에 대한 오류만이 출력된다.

만약 유효성 검사가 모두 통과되었다면 오류 메시지는 모두 사라진다.
5. 컨트롤러에서 유효성 검사 어노테이션 사용하기
@PostMapping("/auth/register")
public String create(@Valid MemberForm memberForm, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
return "member/memberJoin";
}
String password = bCryptPasswordEncoder.encode(memberForm.getPassword());
Member member = Member.createMember(memberForm.getName(), memberForm.getEmail(),
password, memberForm.getNickname());
memberService.join(member);
return "redirect:/auth/login";
}
요청 데이터(MemberForm) 바인딩 시 @Valid에 의해 MemberForm에 존재하는 유효성 검사 어노테이션을 확인한다.
유효성 검사가 실패한다면 오류를 BindingResult에 담아 뷰에 전송한다.
'프로젝트 > Board 프로젝트' 카테고리의 다른 글
| [JPA] 게시판 프로젝트 - 게시글 생성 기능 구현 (7) (4) | 2025.02.03 |
|---|---|
| [JPA] 게시판 프로젝트 - MemberController MockMvc를 사용하여 테스트하기 (6) (3) | 2025.01.27 |
| [JPA] 게시판 프로젝트 - 로그인 및 게시판 메인화면 구현, Spring Security를 사용한 로그인 (4) (3) | 2025.01.17 |
| [JPA] 게시판 프로젝트 - 게시글,댓글 기능 개발 및 테스트 (3) (1) | 2025.01.09 |