'Record'로 DTO만들기

박지성 학부생·2023년 11월 22일
1

BackEnd Develop log

목록 보기
6/27

java-v16 Record에 대한 자바 공식문서 '클릭'

결론부터

레코드 사용

  • 간결하고 불변성을 요구하는 간단한 데이터 전송에 적합
  • 코드의 가독성과 간결함을 우선시할 때 유리

클래스 사용

  • 복잡한 로직, 상태 관리, 확장성이 필요할 때 유리
  • 유연성과 커스텀 로직이 중요한 상황에 적합

코드 비교

class로 dto 생성 (차이 강조를 위해 lombok사용 안했습니다.)

public class UserDto {
    private final String name;
    private final String email;

    public UserDto(String name, String email) {
        this.name = name;
        this.email = email;
    }

    public String getName() {
        return name;
    }

    public String getEmail() {
        return email;
    }

    @Override
    public boolean equals(Object o) {
        ~~~
    }

    @Override
    public int hashCode() {
         ~~~
    }

    @Override
    public String toString() {
        ~~~
    }
}

record로 dto 생성 어노테이션 하나 없이 게터, 세터(기본 생성자), equals(), hashCode(), toString() 알아서 다해줌

public record UserDto(String name, String email) {}

실습 (왜 반환을 저렇게 하냐 의문 ㄴㄴ '유저 생성 결과'에만 집중)

controller

@RequiredArgsConstructor
@RestController
@RequestMapping("/api")
public class UserApiController {
    private final UserService userService;

    @PostMapping("/signup")
    public ResponseEntity<User> signup(@RequestBody AddUserRequest request) {
        User newUser = userService.save(request);

        return ResponseEntity.ok()
                .body(newUser);
    }
}

Service

@RequiredArgsConstructor
@Service
public class UserService {

    private final UserRepository userRepository;

    //    @Transactional
    public User save(AddUserRequest request) {

        BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
        return userRepository.save(request.toEntity());
    }
}

Entity (implements UserDetails은 Security 관련 실습 이어하려고 넣어둠. email, password nickname 제외하고 무시하세요)

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

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", updatable = false)
    private Long id;

    @Column(name = "email", nullable = false, unique = true)
    private String email;

    @Column(name = "password")
    private String password;

    @Column(name = "nickname", unique = true)
    private String nickname;

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

    public User update(String nickname) {
        this.nickname = nickname;

        return this;
    }


    /*
    getAuthorities(): 사용자가 시스템에서 어떤 일을 할 수 있는지 결정하는 '키'와 같은 역할
     ex) 사용자가 어떤 페이지를 볼 수 있거나, 어떤 작업을 수행할 수 있는지를 결정하는 데 사용
        ex) 온라인 쇼핑몰 시스템: 사용자가 로그인하면, 시스템은 getAuthorities()를 호출 => 이 사용자에게 어떤 권한이 있는지 확인
    코드에서 return List.of(new SimpleGrantedAuthority("user"));는 이 사용자가 '일반 사용자(user)' 권한을 가지고 있음을 나타냄
    '일반 사용자' 권한을 가진 사람은 쇼핑몰에서 쇼핑을 할 수 있지만, 상품을 추가하거나 가격을 변경하는 등의 관리자 기능은 사용할 수 없음
    즉, getAuthorities() 메서드는 사용자가 시스템 내에서 할 수 있는 일의 '범위'를 정의
    이를 통해 어떤 사용자가 특정 기능을 사용할 수 있는지, 또는 특정 페이지에 접근할 수 있는지를 시스템이 결정할 수 있게 해줌
    */
    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return List.of(new SimpleGrantedAuthority("user"));
    }

    @Override
    public String getUsername() {
        return email;
    }

    @Override
    public String getPassword() {
        return password;
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return true;
    }
}

RequestDTO

public record AddUserRequest(
        String email,
        String password,
        String nickname
) {

    // 컴팩트 생성자를 사용하여 유효성 검사 수행
    public AddUserRequest {
        if (!isValidEmail(email)) {
            throw new IllegalArgumentException("이메일 형식 지켜라.");
        }
        if (password == null || password.length() <= 4) {
            throw new IllegalArgumentException("비번이 4자리 이상은 돼야지 바보야.");
        }
    }

    private static boolean isValidEmail(String email) {
        String emailRegex = "^([\\w\\.\\_\\-])*[a-zA-Z0-9]+([\\w\\.\\_\\-])*([a-zA-Z0-9])+([\\w\\.\\_\\-])+@([a-zA-Z0-9]+\\.)+[a-zA-Z0-9]{2,8}$";
        return email != null && Pattern.matches(emailRegex, email);
    }

    public User toEntity() {
        return User.builder()
                .email(email)
                .password(password)
                .nickname(nickname)
                .build();
    }
}

config(마찬가지로 무시하세요 이어서 할 security로 그냥 넣어둠)

spring security를 의존성에 넣어놔서 이걸 해놔야 포스트맨으로 실습가능

@Slf4j
@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        // CSRF, 기본 HTTP 인증 설정 비활성화. security6 형식으로 작성.
        http.csrf(csrf -> csrf.disable())
                .httpBasic(httpBasic -> httpBasic.disable());

        http.authorizeHttpRequests(auth -> auth
                .requestMatchers(new AntPathRequestMatcher("/api/signup")).permitAll()
                .requestMatchers(new AntPathRequestMatcher("/api/**")).authenticated()
                .anyRequest().permitAll());
        return http.build();
    }
}

포스트맨 결과

결론2

레코드레코드찍찍찍 레코드레코드찍찍찍

profile
참 되게 살자

4개의 댓글

comment-user-thumbnail
2024년 1월 25일

/

1개의 답글