SMALL

Entity를 생성하다 보면 PK(primary key)가 여러 개가 되는 경우가 발생한다.

이러한 경우에 어떠한 방식으로 Entity를 작성해야 하는지 알아보도록 하자.

 

JPA에서는 두 가지 방식을 제공한다.

@IdClass, @EmbeddedId

각각의 사용법과 특징을 알아보도록 하자.

 

@IdClass

@Getter
@NoArgsConstructor
@Entity
@IdClass(MemberId.class)
public class Member {
	@Column
	private String name;
	
	@Id
	@Column
	private String teamId;
	
	@Id
	@Column
	private String teamName;
}

복합키가 존재하는 Entity에 @IdClass Annotation을 추가해주고, 복합키를 설정할 class명을 정해준다.

또한, 복합키로 지정할 컬럼에 @Id Annotation을 추가한다.

 

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor
public class MemberId implements Serializable {

	private static final long serialVersionUID = 5735022999922182249L;

	@EqualsAndHashCode.Include
	public String teamId;
	
	@EqualsAndHashCode.Include
	public String teamName;
}

IdClass의 작성시 지켜야 할 사항들이 존재한다.

  • Serializable을 implements 받아야 한다.
  • equal, hashCode method를 구현해야 한다. 위의 예제에서는 lombok을 이용하였다.
  • 기본 생성자가 존재해야 한다.
  • public class 이여야 한다.
  • Entity Class의 필드명과 동일한 필드를 가지고 있어야 한다. 

 

public interface MemberRepository extends JpaRepository<Member, MemberId>{

}

Repository를 작성 시에는 JpaRepository의 제너릭 타입에 Entity Class와 Entity Class의 Key값을 넣는데 복합키를 사용한 경우에는 위와 같이 넣어주어야 한다.

 

@EmbeddedId

@Getter
@NoArgsConstructor
@Entity
public class Member {
	@Column
	private String name;
	
	@EmbeddedId
	private MemberId memberId;
}

 

@EqualsAndHashCode(onlyExplicitlyIncluded = true)
@NoArgsConstructor
@Embeddable
public class MemberId implements Serializable {

	private static final long serialVersionUID = 5735022999922182249L;

	@EqualsAndHashCode.Include
	public String teamId;
	
	@EqualsAndHashCode.Include
	public String teamName;
}

 

이렇게 두 가지의 형식이 존재하는데 각각의 선언하는 방식도 다르지만 실제로 쿼리를 조회한 후에 값을 조회할 때도 차이가 존재한다.

 

@IdClass의 경우에는 member.getTeamId()와 같이 사용

@EmbeddedId의 경우에는 member.getMemberId().getTeamId()와 같이 사용해야 한다. 

LIST
SMALL

DTO 데이터 검증이 필요한 이유 

보통 Web Appliation을 작성하면, Front-End에서 사용자가 입력한 정보를 검증하고, 검증이 완료된 데이터를 Back-End에게 넘겨주게 된다.

하지만, 보통의 경우가 아닌 상황에서는 Back-End에서는 입력값으로 받은 DTO를 신뢰할 수가 없게 된다.

( Web Application UI를 통해 요청된 Request가 아닌 경우 - curl, postman 등등..)

이러한 상황에 대한 대비로 Back-End에서도 Request로부터 전달받은 DTO에 대한 검증이 필요하다.

 

@Valid

Spring에서는 DTO 필드에 조건과 메세지를 작성해주면, @Valid를 이용해 데이터 검증을 할 수 있다.

 

@Valid는 JSR-303, 380 표준 스펙으로

제약 조건이 부여된 객체에 대해 Beam Validator를 이용하여 검증하도록 지시하는 Annotation이다.

Spring에서는 LocalValidatorFactoryBean을 이용해 JSR 표준의 검증 기능을 사용한다.

 

SpringBoot 2.3 이상의 버전 부터는 spring-boot-starter-web 의존성 내부에 있던 validation이 사라졌다.

spring-boot-starter-validation 의존성을 따로 추가해 주어야 한다. 

 

Dto

@Getter
@NoArgsConstructor
public class UserSaveRequestDto {

    @NotBlank(message = "Please, insert UserID!")
    private String userId;

    @NotBlank(message = "Please, insert UserName!")
    private String userName;

    @Email(message = "Please, insert valid e-mail!",
            regexp = "^(?=.{1,64}@)[A-Za-z0-9_-]+(\\.[A-Za-z0-9_-]+)*@[^-][A-Za-z0-9-]+(\\.[A-Za-z0-9-]+)*(\\.[A-Za-z]{2,})$")
    private String email;

    @NotBlank(message = "Please, insert password!")
    private String password;

    @Size(min = 1, message = "User must be mapped with Group")
    @NotNull(message = "Please, insert GroupIdList!")
    private Set<String> groupIdList;
}

javax.validation.constraints에서 제공하는 valid 관련 Annotation 

https://docs.jboss.org/hibernate/beanvalidation/spec/2.0/api/

 

 

Controller

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

    @PostMapping("/user")
    public String save(@RequestBody @Valid UserSaveRequestDto requestDto) throws Exception {
        return userService.save(requestDto);
    }
}

검증을 원하는 @RequestBody argument에 @Valid Annotation을 추가해 주면 된다.

 

Exception Handling

@ControllerAdvice
public class CommonExceptionHandler<T> {

    @ExceptionHandler(value = {MethodArgumentNotValidException.class})
    protected ResponseEntity<Response<T>> handleMethodArgNotValidException(
            MethodArgumentNotValidException exception) {

        Response<T> response = new Response<>();
        response.addErrorMsgToResponse(
                exception.getBindingResult().getAllErrors().get(0).getDefaultMessage()
        );

        return ResponseEntity.status(HttpStatus.NOT_ACCEPTABLE).body(response);
    }
}

@RequestBody로 부터 받은 파라미터에 Error가 발생한 경우에는 MethodArgumentNotValidException이 발생한다.

Spring의 Exception Handling 관련 Annotation을 이용하여 해당 Exception에 대한 처리를 추가해 주면 된다.

LIST
SMALL

Spring Cloud Config는 비대칭키, 대칭키 각각 암호화/복호화 방식을 지원한다.

보안 측면에서 public key, private key가 각각 존재하는 비대칭키 방식이 더 안전하다. 

대칭키 방식은 암호화/복호화에 같은 키가 사용되므로 키가 유출됐을 때의 위험이 크다는 단점이 있다. 

 

 

비대칭키 생성 

// private key 생성
keytool -genkeypair -alias privateKey -keyalg RSA -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" -keypass "password123" -keystore privateKey.jks -storepass "password123" -storetype pkcs12

// 인증서 파일 생성
keytool -export -alias privateKey -keystore privateKey.jks -rfc -file trustServer.cer

// 인증서 파일 -> jks 파일 public key 생성
keytool -import -alias trustServer -file trustServer.cer -keystore publicKey.jks

 

Java Cryptography Extension (JCE) 설치

자바에서 제공하는 encrypt를 사용하기 위해서는 JCE 다운로드하여 classpath에 등록해주어야 한다. 

( 11버전 이후부터는 default로 포함되어 있다고 한다)

https://www.oracle.com/java/technologies/javase-jce-all-downloads.html )

 

server - application.yml 설정 추가 ( for encrypt endpoint )

encrypt:
  key-store:
    location: file:../server/key/privateKey.jks # location of key 
    alias: privateKey
    password: password123

 

encrypt endpoint 

 

spring cloud config - properties example 

 

spring cloud config - properties 조회

위의 내용을 그림으로 표현하면 아래와 같다.

spring cloud server에서 encrypt, decrypt를 모두 담당하게 된다.

client에게 properties를 전달할 때 decrypt 된 값들을 전달하는 것이다.

 

 

Server-Encrypt, Client-Decrypt

 

위와 같은 형태가 아닌 server에서는 encrypt된 properties를 들고 있고

client에서 해당 properties를 가져갈때 decrypt 하고 싶은 상황에는 아래와 같이 설정하면 된다.

 

server - application.yml 설정 추가 ( for encrypt endpoint )

spring:
  cloud:
    config:
      server:
        encrypt:
          enabled: false
encrypt:
  key-store:
    location: file:../server/key/publicKey.jks # location of key 
    alias: trustServer
    password: password123

server side에서 decrypt 하는 것을 방지하기 위해 

spring.cloud.config.server.encrypt.enabled = false로 지정 

server side에는 encrypt만 담당하기 위해 public key 등록 

 

 

client-appliation.yml 설정 추가 decrypt

encrypt:
  key-store:
    location: file:../sba-server/key/privateKey.jks
    alias: privateKey
    password: 1q2w3e4r

 

 

LIST
SMALL

Spring Cloud Config를 통해서 설정 값을 중앙에서 관리할 수 있다.

보통은 기본값으로 git을 통한 관리가 이루어진다.

(https://joomn11.tistory.com/79)

 

설정값들 중에 password와 같은 중요 데이터들은 암호화가 필요할 수 도있다.

이러한 경우에 어떻게 해야하는지 알아보도록 하자.

 

Java Cryptography Extension (JCE) 설치

자바에서 제공하는 encrypt를 사용하기 위해서는 JCE 다운로드하여 classpath에 등록해주어야 한다. 

( 11버전 이후 부터는 default로 포함되어 있다고 한다) (https://www.oracle.com/java/technologies/javase-jce-all-downloads.html)

 

 

application.yml 설정 추가 

대칭키를 이용하기 위해서는 application.yml or bootstrap.yml에 해당 설정 추가 

encrypt:
  key: test_key // set user key !

 

encrypt endpoint 

위의 두 설정을 완료한다면 encrypt endpoint를 이용하여 원하는 property를 암호화 할 수 있다.

 

암호화 property 설정

응답으로 온 값을 spring cloud config의 값으로 사용 할 수 있다.

 

 

실제 spring cloud config 서버에 로드된 값들을 조회해보면 아래와 같이 복호화된 형태로 조회가 된다.

 

추가적으로 암호화한 내용을 client로 전달할때 복호화를 하지 않고 건내고 싶은경우에 대해서 알아보자.

아래의 설정은 default값이 true이다.

해당 설정을 false로 바꾼다면 client에게 설정값을 전달할때 암호화 된 형태로 전달할 수 있다.

spring:
  cloud:
    config:
      server:
        encrypt:
          enabled: false

하지만 저 형태로 설정을 한다면, client 측에서는 encrypt.key값을 가지고 있어야지 spring cloud config로 부터 설정 값들을 받을 수 있다. 

해당 설정이 없다면 아래와 같은 exception이 발생한다.

 

 

 

 

LIST

+ Recent posts