Develop/Java & Spring

[Spring Boot] 게시판 프로젝트 v2.0 - JWT를 이용한 회원가입 및 로그인 구현 #2 Member 패키지

jjh0119 2025. 3. 26. 13:22

Member 패키지는 기본적인 회원관리를 위한 클래스들이 담겨있는 패키지로 굉장히 간단한 회원가입 절차만을 구현했기 때문에 클래스들이 복잡하지 않고 각 패키지의 기능이나 역할들에 대해서는 앞선 게시글에서 충분히 다뤘기 때문에 간단하게 소개만 하고 넘어가려고 한다.


#1. Domain

com.security_board.security.member.domain.Member

package com.security_board.security.member.domain;


import com.security_board.security.util.Role;

import jakarta.persistence.*;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@Table(name= "member")
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Builder
public class Member {
	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	@Column(length = 50, nullable = false)
	private String email;
	
	@Column(length = 100)
	private String password;
	
	@Column(length = 20, nullable = false)
	private String name;
	
	@Column(length = 20)
	private String socialId;
	
	@Enumerated(EnumType.STRING)
	private Role role;
}

Board와 마찬가지로 Primary Key 역할을 하는 id와 함께 정말 기본적인 email, password 그리고 유저의 이름만 담는 간단한 entity로 구성했다.


#2. DTO

com.security_board.security.member.dto.MemberDto

package com.security_board.security.member.dto;

import lombok.Builder;
import lombok.Data;

@Data
@Builder
public class MemberDto {
    private String email;
    private String password;
    private String name;

    public static MemberDto postDto(String email, String password, String name) {
        return MemberDto.builder()
                .email(email)
                .password(password)
                .name(name)
                .build();
    }

    public static MemberDto getDto(String email, String name) {
        return MemberDto.builder()
                .email(email)
                .name(name)
                .build();
    }
}

DTO는 등록할 때의 DTO와 다르게 조회할 때는 클라이언트에서 사용할 이메일과 이름만 가져올 수 있도록 만들었다.


#3. Repository

com.security_board.security.member.repository.MemberRepository

package com.security_board.security.member.repository;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.repository.query.Param;

import com.security_board.security.member.domain.Member;

public interface MemberRepository extends JpaRepository<Member, Long>{
	Optional<Member> findByEmail(@Param("email") String email);
	
    Optional<Member> findBySocialId(@Param("socialId") String socialId);
}

#4. Contorller

com.security_board.security.member.contorller.MemberController

package com.security_board.security.member.contorller;

import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.security_board.security.member.dto.MemberDto;
import com.security_board.security.member.service.MemberService;
import com.security_board.security.util.Header;

import lombok.AllArgsConstructor;

@RestController
@AllArgsConstructor
public class MemberController {
	private final MemberService memberService;
	
	@PostMapping("/signup")
	public Header<String> signUp(@RequestBody MemberDto memberDto){
		return memberService.saveMember(memberDto);
	}
}

#5. Service

com.security_board.security.member.service.MemberService

package com.security_board.security.member.service;

import java.util.Optional;

import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import com.security_board.security.member.domain.Member;
import com.security_board.security.member.dto.MemberDto;
import com.security_board.security.member.repository.MemberRepository;
import com.security_board.security.util.Header;
import com.security_board.security.util.MemberMapper;

import jakarta.transaction.Transactional;
import lombok.AllArgsConstructor;

@Service
@Transactional
@AllArgsConstructor
public class MemberService {
	
	private final MemberRepository memberRepository;
	private final PasswordEncoder passwordEncoder;
	
	public Header<String> saveMember(MemberDto memberDto) {
		if(this.searchMember(memberDto.getEmail()).isPresent()) {
			return Header.ERROR("동일한 이메일이 존재합니다");
		}
		memberRepository.save(MemberMapper.toEntityFromPostDto(memberDto, passwordEncoder));
		return Header.OK("성공적으로 가입되었습니다");
	}
	
	public Optional<Member> searchMember(String email){
		return memberRepository.findByEmail(email);
	}
	
	public boolean validatePassword(String rawPassword, Member member) {
	    return passwordEncoder.matches(rawPassword, member.getPassword());
	}
}

#6. Util

com.security_board.security.util.MemberMapper

package com.security_board.security.util;

import org.springframework.security.crypto.password.PasswordEncoder;

import com.security_board.security.member.domain.Member;
import com.security_board.security.member.dto.MemberDto;

public class MemberMapper {
    public static MemberDto toGetDtoFromEntity(Member member) {
        return MemberDto.getDto(
            member.getEmail(),
            member.getName()
        );
    }
    
	public static Member toEntityFromPostDto(MemberDto memberDto, PasswordEncoder passwordEncoder) {
		Member member = Member.builder()
				.email(memberDto.getEmail())
				.password(passwordEncoder.encode(memberDto.getPassword()))
				.name(memberDto.getName())
				.role(Role.USER)
				.build();	
		return member;
	}
}

com.security_board.security.util.Role

package com.security_board.security.util;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum Role {
    ADMIN("ROLE_ADMIN", "관리자"),
    USER("ROLE_USER", "일반 사용자"),
    NOT_REGISTERED("ROLE_NOT_REGISTERED", "회원가입 이전 사용자");

    private final String key;
    private final String title;
}