[React + SpringBoot] JWT 인증 구현 ② - Entity, DTO

SihoonCho·2023년 4월 19일
0
post-thumbnail

※ 읽기에 앞서


본 시리즈는 작성자의 이해와 경험을 바탕으로 실습 위주의 설명을 기반으로 작성되었습니다.
실습 위주의 이해를 목표로 하기 때문에 다소 과장이 많고 생략된 부분이 많을 수 있습니다.
따라서, 이론적으로 미흡한 부분이 있을 수 있는 점에 대해 유의하시기 바랍니다.

또한, 본 시리즈는 ChatGPT의 도움을 받아 작성되었습니다.
수 차례의 질문을 통해 도출된 여러가지 다양한 방식의 코드를 종합하여
작성자의 이해와 경험을 바탕으로 가장 정석으로 생각되는 코드를 재정립하였습니다.


📌 Entity


User EntityAuth Entity를 생성합니다.
User.java는 말 그대로 유저 정보를 저장하는 용도로 사용하고,
Auth.java는 JWT 인증을 위한 정보를 저장하는 용도로 사용합니다.


📖 User.java


Role.java

public enum Role {
    ROLE_USER("USER"),
    ROLE_ADMIN("ADMIN");
    
    // "USER", "ADMIN"
    private String value;
    
    // Constructor
    Role(String value) {
    	this.value = value;
    }
    
    // GetValue
    public String getValue() {
    	return this.value;
    }
}

User.java

@NoArgsConstructor
@Getter
@Entity
public class User extends BaseTime {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(length = 50, nullable = false, unique = true)
    private String email;
    
    @Column(length = 50, nullable = false, unique = true)
    private String contact;
    
    @Column(length = 50, nullable = false, unique = true)
    private String username;
    
    @Column(length = 100, nullable = false)
    private String password;
    
    @Enumerated(EnumType.STRING)
    private Role role;
    
    @OneToOne(mappedBy = "user", cascade = CascadeType.REMOVE)
    private Auth auth;

    @Builder
    public User(String email, String contact, String username, String password, Role role) {
    	this.role = role;
        this.email = email;
        this.contact = contact;
        this.username = username;
        this.password = password;
    }
}

📖 Auth.java


Auth.java

@NoArgsConstructor
@Getter
@Entity
public class Auth extends BaseTime {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(nullable = false)
    private String tokenType;

    @Column(nullable = false)
    private String accessToken;

    @Column(nullable = false)
    private String refreshToken;

    @OneToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;

    @Builder
    public Auth(User user, String tokenType, String accessToken, String refreshToken) {
        this.user = user;
        this.tokenType = tokenType;
        this.accessToken = accessToken;
        this.refreshToken = refreshToken;
    }
}

각각의 필드는 다음과 같은 역할을 합니다.

  • tokenType: JWT 사용시 추후 Bearer 타입을 가지게 됩니다.
  • accessToken: 액세스 요청을 위한 수명이 짧은 Token
  • refreshToken: 새 accessToken을 발급받기 위한 수명이 긴 Token
  • expirationDate: accessToken, refreshToken 만료 일자

Access Token vs Refresh Token 차이

Access Token은 인증에 성공한 후 사용자에게 발급되는 단기 토큰입니다. 일반적으로 보호된 리소스 또는 서비스에 대한 요청을 인증하는 데 사용됩니다. 액세스 토큰은 도난당하고 악의적으로 사용될 위험을 최소화하기 위해 수명이 짧습니다. 액세스 토큰이 만료되면 사용자는 새 토큰을 얻기 위해 다시 인증해야 합니다.

반면, Refresh Token은 새 액세스 토큰을 얻는 데 사용되는 수명이 긴 토큰입니다. 액세스 토큰이 만료되면 사용자는 새로 고침 토큰을 사용하여 재인증 없이 새 액세스 토큰을 얻을 수 있습니다. 이것은 인증 요청의 빈도를 줄이고 시스템을 보다 사용자 친화적으로 만듭니다.

[Reference] ChatGPT
Q. What's the difference between accessToken and refreshToken?


📌 UserDTO


User와 관련된 Request, Response에 관여하는 데이터 객체입니다.
Database와 직접적으로 연결되어 있지 않기 때문에 안전성 측면에서 용이합니다.


📖 UserRequestDTO.java


UserRequestDTO.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class UserRequestDTO {
    private Role role;
    private String email;
    private String contact;
    private String username;
    private String password;

    public User toEntity() {
        return User.builder()
                .role(this.role)
                .email(this.email)
                .contact(this.contact)
                .username(this.username)
                .password(this.password)
                .build();
    }
}

회원가입 및 로그인에 사용되는 데이터 객체입니다.
회원가입 등의 DB 생성 요청이 발생할 경우, DTO객체에서 Entity객체로 변환할
toEntity() 메소드를 미리 하나 구현합니다.


📖 UserResponseDTO.java


UserResponseDTO.java

@Getter
@Setter
public class UserResponseDTO {
    private Long id;
    private String role;
    private String email;
    private String contact;
    private String username;
    
    public UserResponseDTO(User entity) {
    	this.id = entity.getId();
        this.email = entity.getEmail();
        this.contact = entity.getContact();
        this.username = entity.getUsername();
        // Enum -> String
        this.role = entity.getRole().name();
    }
}

로그인 후 응답에 사용되는 데이터 객체입니다.

추후 Service에서 요청에 따라 Database로부터 Entity 정보를 추출한 후,
응답 객체 ResponseDTO를 생성할 때, 생성자의 매개변수로 각각의 필드에 해당하는 값이 아닌, 추출한 Entity 객체를 사용함으로써 매개변수 개수에 따른 긴 길이의 생성 코드를 단축합니다.

Enum Type RoleString Type으로 변환하여 반환합니다.


📌 AuthDTO


Auth와 관련된 Request, Response에 관여하는 데이터 객체입니다.
Database와 직접적으로 연결되어 있지 않기 때문에 안전성 측면에서 용이합니다.


📖 AuthRequestDTO.java


AuthRequestDTO.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AuthRequestDTO {
    private String username;
    private String password;
}

클라이언트측에서 서버측으로 API 요청을 보낼 때 사용될 DTO입니다.
서버측 API는 사용자를 인증하고 accessToken을 생성하기 위해 username, password를 사용할 것이기 때문에 AuthRequestDTOusername, password 필드가 존재해야 합니다.

일반적으로 최초 로그인시 API 요청과 함꼐 전달되는 username, password로그인 요청인증 요청을 동시에 Request하며 함께 보냅니다.


📖 AuthResponseDTO.java


AuthResponseDTO.java

@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class AuthResponseDTO {
    private String tokenType;
    private String accessToken;
    private String refreshToken;
    
    @Builder
    public AuthResponseDTO(Auth entity) {
    	this.tokenType = entity.getTokenType();
        this.accessToken = entity.getAccessToken();
        this.refreshToken = entity.getRefreshToken();
    }
}

인증 요청에 대해 User 정보와 함께 Token 정보를 응답으로 반환합니다.

profile
꾸준히 노력하는 개발자

0개의 댓글