스프링 부트로 블로그 서비스 개발하기

회원가입 기능 - 회원 도메인, 이메일 VO 헥사고날화

exena 2025. 11. 26. 17:58

https://www.inflearn.com/course/토비-클린스프링-도메인모델패턴-헥사고날-part1/dashboard

토비님의 강의를 듣고 기존의 게시판 프로젝트를 갈아엎고자 한다.

도커 데스크탑 설치하고 스프링 이니셜라이저에서 도커 컴포즈 라이브러리를 추가해서 프로젝트를 만들었다. MySQL은 따로 설치하지 않아도 도커가 만들어준다. 물론 도커를 킨 채로 프로젝트를 실행해야 한다.

이 화면은 스프링 시큐리티가 자동으로 만들어주는 화면이다. 따로 설정하지 않으면 기본 아이디는 user, 패스워드는 랜덤하게 생성되어 콘솔 창에 출력된다.

다시 여기부터 시작해보자. 아무것도 없는 곳에 컨트롤러를 만들었다. 

축약어를 추가해서 편하게 코딩을 할 수 있다.

일단 기본적인 회원 클래스를 만들고 그 회원의 권한 클래스를 만들어주자.

권한은 Enum으로 만들어도 되지만, 클래스로 만드는 쪽이 DB에서 추가할 수 있어서 확장성이 좋다.

@Entity
@Getter
@ToString
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String email;

    private String nickname;

    private String passwordHash;

    @ManyToMany
    @JoinTable(
            name = "member_role",
            joinColumns = @JoinColumn(name = "account_id"),
            inverseJoinColumns = @JoinColumn(name = "role_id"))
    private Set<Role> roles;

    public static Member register(String email, String nickname, String password, Set<Role> roles) {
        Member member = new Member();

        member.email = Objects.requireNonNull(email);
        member.nickname = nickname;
        member.passwordHash = Objects.requireNonNull(password);
        // member.passwordHash = passwordEncoder.encode(requireNonNull(registerRequest.getPassword()));
        member.roles = roles;

        return member;
    }
}

Objects.requireNonNull을 사용하면 Null을 받지 않도록 만들 수 있다.

도메인에서는 아주 세세한 인풋 검증을 수행하진 않는다. 최소한의 검증 혹은 변할 가능성이 적은 핵심 도메인 규칙 검증만을 수행하는 편이다. 이곳에서는 Null 체크만 했다.

일단 간단하게 구현하긴 했는데 실제로는 스프링 시큐리티의 passwordEncoder를 register 함수에서 패러미터로 받아서 암호화해서 넣어줘야 한다. 아직 스프링 시큐리티에서 passwordEncoder 빈을 등록해주지 않았기 때문에 이부분은 주석 처리하겠다.

여기에 추가적으로 닉네임 변경, 비밀번호 변경 등의 함수가 들어갈 수 있다.

@Entity
public class Role {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name; // ROLE_USER, ROLE_ADMIN 등
}

일단은 간단하게 만들어본 후 테스트 코드를 돌려보자.

권한의 종류는 DB로 관리할 생각이라서 클래스로 만들었는데, 동적으로 추가하는 기능 따위를 넣지 않을 생각이라면 Enum으로 만들어도 상관없다.

간단한 생성 테스트는 별 문제 없이 통과한다.

spotbugs를 plugin에 넣어주고

package-info에 @NonNullApi 어노테이션을 붙여주면 해당 패키지의 모든 함수의 패러미터에서 spotbugs가 null체크를 수행한다.

인텔리제이 Ultimate의 DB 접속 기능을 사용해보자.

테이블 생성(ddl-auto) 설정을 킨 채로 프로젝트를 실행하고 DB를 새로고침 해보면 테이블이 자동으로 만들어진 것을 볼 수 있다. Member, Role, 그리고 그 둘을 다대다로 묶어주는 MemberRole 테이블까지 만들어졌다.

 

추가)

이메일을 값 객체로 만들어줬다.

하는 행동은 이메일 형식에 맞는지 검사하는게 다다.

그리고 멤버 필드에 @Embedded와 @NaturalId를 붙여서 넣어줬다.

도메인 주도 설계에서 제일 특징적인 부분은 도메인 객체 안에 여러 메서드가 들어간다는 점이지만, 값 객체가 많이 쓰인다는 것도 특징이다. 도메인 주도 설계에서 도메인 규칙에 따른 인풋 검증은 도메인 생성자에서 이루어지기 때문에 필연적으로 값 객체가 쓰이게 된다.