[JAVA] UUID는 무엇이고 왜, 어떻게 사용하는가? (메인프로젝트 코드 리팩토링(1)-UUID 적용하기💡)

박두팔이·2023년 6월 25일
0

메인프로젝트기간 이 후, 우리 백엔드팀은 코드리팩토링 하는 시간을 추가로 갖기로 하였다.

그 중 하나가 바로, userId를 외부로 전달할 때 규칙적이고 통일성이 있는userId대신 자바에서 제공하는 UUID를 코드에 적용시키는 것이다.

💡UUID를 사용하는 이유?
예를들면, a라는 회원과 b회원이 거의 동시에 회원가입을 한다고 가정을 하자. 이런경우 a회원의 userId에 +,-1은 b의 userId가 될 것이다.

따라서 a회원은 b회원의 userId를 유추하여 마음대로 쿼리문을 보낼 수 있다. 이러한 보안상의 이유로 규칙성이 없는 UUID를 사용하는 것이다.

우리팀은 사실 서버 내부적으로 user를 조회하기 때문에 UUID까지는 구현할 필요는 없었다. 클라이언트쪽에서 uuid를 사용해야 하는 기능이 구현되어있지 않았음에도 불구하고 우리팀이 UUID를 적용시킨 이유는 학습의 목적도 있지만 추후, 이력서를 통한 사이드프로젝트 매칭, 기업과 개인의 매칭게시판 등 초기 기획의도대로 서비스를 확장시킬 때 외부에서 user의 Id를 사용하게될 가능성이 있기 때문에 UUID를 제인했다.


UUID란?

범용 고유 식별자(Universally Unique Identifiers)

UUID란, 범용 고유 식별자라는 의미이다. 즉, 식별 가능한 고유값이라는 뜻이다.

  • 일련의 숫자와 문자로 구성된 128비트 값으로 이뤄져있다.
  • 이 값은 거의 중복될 가능성이 없는 매우 낮은 확률로 고유성을 보장받기 때문에 충돌 없이 식별자를 생성할 수 있게한다.
  • 또한, 전 세계에서 고유한 값으로 취급되기 때문에 여러 시스템간에 데이터를 교환하거나 통합할 때 매우 유용하다.
  • UUID는 무작위로 생성되며 시간, 장치의 고유 번호 등 다양한 요소를 기반으로 생성된다. 따라서 예측이 불가능하며 보안에도 도움이 된다.

Java에서 UUID 사용방법?

'Java.util.UUID'

클래스를 사용하여 UUID를 생성하고 조작할 수 있다. 이 때, UUID를 문자열로 변환하거나 반대로 문자열에서 UUID로 변환하는 기능을 제공한다.

결론: UUID는 개발자가 고유한 식별자를 생성하고 데이터를 관리하는데 도움을 주는 유용한 도구다!


userId에서 uuid로 변경하면서 발생한 문제?

기존 userId를 클라이언트에게 제공하는 코드에서 UUID클래스를 사용하여 uuid를 response하는 코드로 변경하였다.

@Entity
@Table(name = "member")
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Component
public class User extends Auditable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long userId;
    
    @Column(nullable = false, unique = true)
    private String uuid = UUID.randomUUID().toString(); //UUID가 사용된 부분

이에따라 외부로 userId를 노출시키지 않기 위해서는 기존 userId를 사용하던 곳을 uuid로 변경하는 작업이 필요했다.

🚨 그 중 문제가 되었던 곳이 UserController에서 사용한 코드였다.

@PreAuthorize("#userId == authentication.principal.userId")

기존의 코드에서는 controller메서드가 실행되기 전 Spring Security에서 제공하는 @PreAuthorize를 사용하여 인증된 사용자가 자신의 정보에만 접근할 수 있도록 userId와 로그인된 사용자가 일치하는지 확인 후, 일치하지 않으면 메서드는 실행되지 않고 403에러를 발생시키도록 로직을 작성했다.

userId를 uuid로 수정하게 되면서 아래와 같은 코드로 변경되었다.

@PatchMapping("/my-page/password/{uuid}")
    @PreAuthorize("#uuid == authentication.principal.uuid") // 수정 된 부분
    public LocalDate changePassword(Authentication authentication,
                                    @PathVariable("uuid") String uuid,
                                    @Valid @RequestBody UserPasswordPatchDto userPasswordPatchDto) {
        return defaultUserService.changePassword(uuid, userPasswordPatchDto);
    }

이 애너테이션을 사용하기 위해서는 우리가 사용중인 JwtVerificationFilter의 setAuthenticationToContext 메서드에서 AuthenticationToken에 Principal로 커스텀하게 정의한 UserDetails를 넣어줌으로써 userId를 set하여 가능한 것인데 처음에 이부분을 놓쳐서 500에러를 계속 만났다.

💡 따라서 userId를 set했던 기존의 코드에서 uuid를 set하도록 수정하였다.

private final class UserDetails extends User implements org.springframework.security.core.userdetails.UserDetails {
        UserDetails(User user) {
            setUuid(user.getUuid()); // 수정된 코드
            setEmail(user.getEmail());
            setPassword(user.getPassword());
            setRoles(user.getRoles());
            setName(user.getName());
        }

UUID 코드 적용 결과

휴. 뿌듯

profile
기억을 위한 기록 :>

0개의 댓글