spring

[Spring] 중복 확인 Rest API 설계

허몽구 2023. 6. 20. 19:32

아이디와 닉네임에 대해 중복을 확인할 수 있는 Rest API이다.

 

1. Entity

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Table(name="User")
public class User {

    @Id
    @Column(name="user_id", unique = true)
    private String id; // 아이디

    @Column(name = "user_nickname", unique = true, length = 20)
    private String nickname; // 닉네임

    @Builder
    public User(String id, String nickname){
        this.id = id;
        this.nickname = nickname;
    }
}

아이디와 닉네임 컬럼을 생성해준다.

기본생성자는 동일 패키지나 파생 패키지에서만 사용할 수 있도록 PROTECTED로 설정해줬다.

 

2. Repository

public interface UserRepository extends JpaRepository<User, String> {
    Optional<User> findByNickname(String nickname);

    Optional<User> findById(String id);
}

서비스에서 사용하기 위해 findByNickname과 findById를 레포지토리에 생성해준다. 

 

3. Service

@Service
@RequiredArgsConstructor
public class UserService {

    private final UserRepository userRepository;

    public boolean checkDuplicateNickname(String username) {
        return !userRepository.findByNickname(username).isPresent();
    }

    public boolean checkDuplicateId(String id){
        return !userRepository.findById(id).isPresent();
    }
}

레포지토리에서 닉네임이나 아이디를 찾은 후, 그 닉네임이나 아이디가 존재한다면 false를 리턴시키도록 설계했다.

 

4. ApiResponseDto

@Getter
@RequiredArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PROTECTED)
public class ApiResponseDto<T> {

    private final int code;
    private final String message;
    private T data;

    public static ApiResponseDto success(SuccessStatus successStatus) {
        return new ApiResponseDto<>(successStatus.getHttpStatus().value(), successStatus.getMessage());
    }

    public static <T> ApiResponseDto<T> success(SuccessStatus successStatus, T data) {
        return new ApiResponseDto<T>(successStatus.getHttpStatus().value(), successStatus.getMessage(), data);
    }

    public static ApiResponseDto error(ErrorStatus errorStatus) {
        return new ApiResponseDto<>(errorStatus.getHttpStatus().value(), errorStatus.getMessage());
    }
    
    public static <T> ApiResponseDto<T> error(ErrorStatus errorStatus, T data) {
        return new ApiResponseDto<T>(errorStatus.getHttpStatus().value(), errorStatus.getMessage(), data);
    }
}

요청에 대해 공통적으로 응답을 보낼 수 있도록 ApiResponseDto를 생성해줬다. 성공과 에러 발생 시에 대해 모두 생성해줬다.

 

5. SuccessStatus

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum SuccessStatus {

    CREATE_ID_SUCCESS(HttpStatus.OK, "사용 가능한 아이디입니다."),

    CREATE_NICKNAME_SUCCESS(HttpStatus.OK, "사용 가능한 닉네임입니다.")
    ;

    private final HttpStatus httpStatus;
    private final String message;
}

성공했을 때 어떤 HTTP 상태코드와 메세지를 보낼지 작성해줬다.

 

6. ErrorStatus

@Getter
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
public enum ErrorStatus {
    /*
    CONFLICT
     */
    CONFLICT_ID_EXCEPTION(HttpStatus.CONFLICT, "이미 등록된 id입니다."),
    CONFLICT_NICKNAME_EXCEPTION(HttpStatus.CONFLICT, "이미 등록된 닉네임입니다.")
    ;

    private final HttpStatus httpStatus;
    private final String message;
}

중복됐을 때 에러 메세지를 처리하기 위해 ErrorStatus 코드도 작성해줬다.

 

7. Controller

@RestController
@RequiredArgsConstructor
public class UserController {
    private final UserService userService;
    private final UserRepository userRepository;

    @ApiOperation(value = "아이디 중복 확인", notes = "해당 id가 이미 존재한다면 true, 존재하지 않다면 false를 반환합니다.")
    @ApiImplicitParam(name = "id", value = "사용자가 생성한 id")
    @GetMapping("/checkDuplicateId/{userId}") // 아이디 중복 확인
    public ApiResponseDto<String> checkDuplicateId(@PathVariable String userId) {
        return userService.checkDuplicateId(userId) ? ApiResponseDto.success(SuccessStatus.CREATE_ID_SUCCESS, userId)
                : ApiResponseDto.error(ErrorStatus.CONFLICT_ID_EXCEPTION, userId);
    }

    @ApiOperation(value = "닉네임 중복 확인", notes = "해당 닉네임이 이미 존재한다면 true, 존재하지 않다면 false를 반환합니다.")
    @ApiImplicitParam(name = "nickname", value = "사용자가 생성한 닉네임")
    @GetMapping("/checkDuplicateNickname/{nickname}")
    public ApiResponseDto<String> checkDuplicateNickname(@PathVariable String nickname) {
        return userService.checkDuplicateNickname(nickname) ? ApiResponseDto.success(SuccessStatus.CREATE_NICKNAME_SUCCESS, nickname)
                : ApiResponseDto.error(ErrorStatus.CONFLICT_NICKNAME_EXCEPTION, nickname);
    }
}

응답은 ApiResponseDto에 만들어뒀던 success나 error로 처리를 할 것이다.

우선 isDuplicateId / isDuplicateNickname 변수에 서비스단에서 만들어줬던 중복 처리를 해준다.

존재한다면 false, 존재하지 않다면 true를 리턴한다. 즉 false일 경우 사용 불가능하고 true일 경우 사용할 수 있다.

그 결과를 삼항연산자에 담에 true일 시 success에 데이터를 넘기고 false일 경우 error에 데이터를 넘겨 처리한다.

SuccessStatus와 ErrorStatus에 작성해뒀던 enum을 토대로 코드를 작성하면 된다.

참고로 @ApiOperation과 @ApiImplicitParam은 스웨거 사용을 위한 어노테이션이니 생략해도 된다!

 

그럼 중복 확인이 잘 되는지 포스트맨으로 확인해보자!

다음과 같이 회원가입을 해줬다. 아이디는 "monggu", 닉네임은 "jy"이다.

 

아이디 중복 확인을 해보자.

"monggu"를 사용하려고 하면 이미 등록된 id라는 에러 메세지가 잘 뜬다.

 

다른 아이디를 사용해보자.

중복되지 않는 아이디는 사용 가능하다는 메세지가 출력되는 것을 확인할 수 있다.

 

이번엔 닉네임 중복 확인을 해보자!

 

회원가입할 때 사용했던 "jy" 닉네임은 이미 등록된 닉네임이라며 사용할 수 없다는 메세지가 출력된다.

 

중복되지 않는 닉네임을 사용하면 사용 가능한 닉네임이라고 출력되는 메세지를 확인할 수 있다!