안녕하세요. 씨엔스 입니다.

JPA를 사용하여 Entity 를 만들때 공통적으로 사용되는 컬럼들을 중복해서 사용하지 않고 상속받아 사용할 수 있도록 도와주는 MappedSuperClass 어노테이션에 대해 알아보려고 합니다.

테이블을 생성할 때 공통적으로 사용되는 컬럼들이 존재하고 이를 매번 Entity에 포함하는 경우 코드가 중복되는 경우가 생깁니다.

이때 @MappedSuperClass 를 사용하여 중복되는 코드를 줄이고 공통된 객체를 상속받아 매핑할 수 있습니다.

Board 와 Comment Entity 사이의 공통 속성 객체 BaseSystemFieldEntity 구조

위 이미지는 Board와 Comment라는 두개의 Entity 사이에서 공통적으로 사용되는 속성 객체를 BaseSystemFieldEntity라는 부모로 사용하는 구조를 표현하였습니다.

이를 간단한 소스코드로 표현하자면

 

> BaseSystemFieldEntity

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
@EntityListeners(AuditingEntityListener.class)
@MappedSuperclass // @Entity 대신 사용한다.
public class BaseSystemFieldEntity {
    @Column(name = "register_id", updatable = false)
    private String registerId;
    @Column(name = "regist_dt", updatable = false)
    private LocalDateTime registDt;
    @Column(name = "updusr_id")
    private String updusrId;
    @Column(name = "updt_dt")
    private LocalDateTime updtDt;
 
    ...
}
 
cs          

 

> Board

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@Entity
public class Board extends BaseSystemFieldEntity {
    @Id
    @Column(name = "bbs_id")
    private String bbsId;
    @Column(name = "bbs_sj")
    private String bbsSj;
    @Column(name = "bbs_cn")
    private String bbsCn;
    @Column(name = "use_at")
    private String useAt;
}
cs

 

> Comment

1
2
3
4
5
6
7
8
9
10
11
12
13
@Entity
public class Comment extends BaseSystemFieldEntity {
    @Id
    @Column(name = "cmnt_id")
    private String cmntId;
    @Column(name = "bbs_id")
    private String bbsId;
    @Column(name = "cmnt_cn")
    private String cmntCn;
}
cs

 

BaseSystemFieldEntity 클래스는 테이블로 매핑할 필요가 없고 @Entity 어노테이션 대신 @MapperSuperClass 을 사용하면됩니다.

또한 @Entity 어노테이션을 사용한 클래스에 BaseSystemFieldEntity 클래스를 상속받아 사용합니다.

하지만 @MapperSuperClass 이 ORM의 상속 관계와는 달리 자식 클래스의 매핑정보만 제공하는 역할이기 때문에 테이블과는 관련이 없습니다. 즉, 테이블과는 관계가 없다는 단순 매핑 역할일 뿐입니다.(@Entity 사용X) 

그렇기에 직접 생성해서 사용할 일이 없으므로 추상 클래스로 사용하는 것을 권장합니다.

'Language > Java' 카테고리의 다른 글

[Swagger docs]Spring 3.x.x 버전에서 swagger 사용법  (1) 2023.12.27
[Java] Null 체크를 위한 방법  (1) 2022.03.31

안녕하세요. 오랜만에 인사드립니다.

 

개인적으로 많은 일들이 있던 2023년이라서 너무나도 오랫동안 블로그를 못쓰고 있었네요. 자세한 이야기는 따로 썰을 풀어서 글을 써볼까 합니다 ㅎㅎ

 

일단 이 글의 목적은 개발을 진행하면서 API 문서로 swagger를 많이 사용하고 있는데, Spring 3버전 이상부터는 기존에 사용하던 springfox 가 아닌 springdoc-openapi-ui 라이브러리를 사용해야 한다는 내용을 소개하려고 합니다!

 

"swagger 사용법" 이라고 검색하게 되면 대부분이 springfox를 이용한 swagger 사용법들이 나오고 있어 저도 그렇게 사용하는줄 알았는데, 이제는 springdoc을 사용하여 swagger를 써야한다는 걸 알게되었는데요.

 

이유는 springfox는 2020년 7월 14일에 3.0.0 버전을 마지막으로 더이상 업데이트가 되지 않는다는 것을 알 수 있습니다.

(참고: https://github.com/springfox/springfox)

 

그에 비해 springdoc-openapi 의 경우 제가 작성하는 날짜를 기준(23년12월27일)으로 3주전까지 업데이트가 되고 있습니다.

(참고: https://github.com/springdoc/springdoc-openapi)

그러기에 Spring 버전이 올라가더라도 적용이 가능한 springdoc-openapi 사용이 필수적이라고 보여집니다.

 

간단한 사용법을 설명드리자면 먼저 해당 라이브러리를 적용해야합니다.

 

Gradle / Maven 에 dependency 를 추가하시면 됩니다.

공식 springdoc Github 에서 공식 문서로 제공해주고 있는데

 

- Gradle

## implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.3.0'

 

- Maven

<dependency>

     <groupId>org.springdoc</groupId>

     <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>

     <version>2.3.0</version>

</dependency>

 

으로 추가하여 사용하실 수 있습니다.

 

그리고 SwaggerConfig.java 라는 파일을 생성하여 설정 할 수도 있습니다.

 

import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.info.Info;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SwaggerConfig {
    @Bean
    public OpenAPI openAPI(){
        Info info = new Info()
                .title("Spring Boot 를 이용한 API 확인 Swagger")
                .version("0.1")
                .description("API 문서입니다. \n Demo 버전으로 들고다니는 용도입니다.");
        return new OpenAPI()
                .components(new Components())
                .info(info);
    }
}

 

위와 같은 형식으로 파일을 생성하여 사용하시면 

url에 http://localhost:사용자port/swagger-ui/index.html 를 입력하여 들어가면 

 

 

이런 화면이 나타나며 swagger를 사용하실 수 있습니다.

 

2버전과는 조금 달라진 ui가 있지만 크게 차이가 없기 때문에 사용하시는데 어려움은 없으실테고,

Spring 3 버전 이후 부터는 springfox가 아닌 springdoc-openapi 라이브러리를 사용하시는걸 잊지 마시고 헷갈리지 않으셨으면 합니다.

 

감사합니다 :)

안녕하세요. 씨엔스입니다.

 

우리가 개발을 할 때 가장 많이 발생하는 예외 처리 중 하나가 바로 NullPointerException 이라 생각합니다.

Null 오류를 발생시키지 않기 위해서 지금까지는 분기처리를 통해 Null 체크 여부를 확인했는데,

자바 8 버전에서 나온 Optional 클래스를 통해 NPE 방지를 알아보도록 하겠습니다.

 

 

  • Java 8 버전 이전 null 예외처리 방식
1
2
3
4
5
6
7
8
9
10
11
class CodeRunner{
    public static void main(String[] args){
        List<String> names = null;
        List<String> list = getNames();         
        // null 인 경우
        if(list != null) {
            // next step
        }
    }
}
 
cs

ㅇㅇㅇㅇㅇ

기존에는 null이 발생하는 상황이 생길 경우 if문을 이용하여 null을 체크 하지만, 이와 같은 경우 코드의 가독성이 떨어지고 해당 객체가 null을 가질 수 있는 객체인지, 필수 값인지 직관적으로 알 수 있는 방법이 없어 오류를 찾더라도 해결하기에 어려움이 발생할 수 있었습니다.

 

  • Java 8 버전에 나온 Optional을 사용한 방식
1
2
3
4
5
6
7
8
9
10
class CodeRunner{
    public static void main(String[] args){
        List<String> list = getNames();
        // list 가 null 인지 아닌지 체크
        Optional <List<String>> optional = Optional.ofNullable(list);
        System.out.println(optional); // Optional.empty 
        System.out.println(optional.isPresent()); // false    
    }
}
 
cs

 

Java 8 버전에 처음 도입된 Optional은 값이 존재할 수도 있고, 아닐 수도 있는 값을 객체로 포장해주는 클래스입니다.

null 체크를 직접 할 필요가 없고 NPE가 발생할 가능성이 있는 값을 다룰 필요가 없다는 장점이 있습니다.

 

 

Optional 사용하기

 

  • Optional.empty() : 비어있는 Optional 객체 생성하기(return : Optional.empty)
1
Optional<String> opt = Optional.empty(); // 비어있는 Optional 객체를 생성
cs

 

  • Optional.of() : Null 값이 없는 Optional 객체 생성하기(값이 null 이면 NPE 발생)
1
Optional<String> opt = Optional.of("value 존재"); // 값이 존재하는 Optional 객체 생성(Optional[value 존재])
cs

 

  • Optional.ofNullable() : null 이거나 값이 있을 수 있는 객체를 생성한다. (null 여부를 확신할 수 없을 때)
1
2
3
// Optional의 value는 값이 있을 수도 있고 null 일 수도 있다. 
Optional<String> optional1 = Optional.ofNullable(null); 
Optional<String> optional2 = Optional.ofNullable("value 존재");
cs

 

 

Optional 값 체크하기

 

  • get() : Optional의 값을 가져오고, 비어있는 Optional 객체에 대해서는 NoSuchElementException 오류가 발생한다.
1
2
Optional<String> opt = Optional.of("value 존재");  // Optional 값이 존재
System.out.println(opt.get()); // return value 존재
cs

 

  • orElse() : 비어있는 Optional 객체에 대하여 orElse로 넘어온 값을 반환한다.
1
2
Optional<String> opt1 = Optional.empty(); // 빈 Optional 객체
System.out.println(opt1.orElse("value")); // return value
cs

 

  • orElseGet() : 비어있는 Optional 객체에 대하여 orElseGet로 넘어온 함수형 값을 통해 생성된 객체를 전달한다.
1
2
Optional<String> opt2 = Optional.empty(); // 빈 Optional 객체
System.out.println(opt2.orElseGet(() -> new String("새로운 값"))); // return value
cs

 

  • orElseThrow() : 비어있는 Optional 객체에 대하여 orElseThrow로 넘어온 함수형 값을 통해 오류를 발생시킨다.
1
2
Optional<String> opt3 = Optional.empty(); // 빈 Optional 객체
System.out.println(opt3.orElseThrow(RuntimeException::new)); // return value
cs

 

  • map() : Optional 객체의 값을 원하는 형태로 형변환을 할 수 있다.
1
2
Optional<String> opt = Optional.of("TEST"); // Optional 객체 값 존재할 경우
System.out.println(opt.map(String::length)); // Optional 객체의 형태를 변경
cs

 

  • filter() : filter 메소드의 인자로 받은 람다 식이 참이면 Optional 객체를 그대로 통과시키고 거짓이면 Optional.empty()를 반환해서 추가로 처리가 안되도록 한다.
1
2
Optional<String> opt = Optional.of("test");
System.out.println(opt.filter(v -> v.equals("TEST"))); // true : Optional[TEST] , false : Optional.empty
cs

 

 

정리

 

  1. Optional 객체를 사용할 경우 null을 선언할 필요가 없습니다.
  2. 반환하는 경우에만 사용하자.
  3. 상황에 맞게 사용해야한다. (남발하지말라, 오히려 독이 된다.)

 

아직 실무에서 적용해본 적이 없어 저도 미숙하기에 이렇게 정리를 하면서 공부해보았는데, 실제 소스에서의 null 처리를 Optional을 통해 진행해볼까 합니다.

감사합니다.

 

 

-참고자료

 

 

+ Recent posts