[Spring] - Optional

๐Ÿ’ญ Opiotnal์ด๋ž€?

Spring ์˜ˆ์‹œ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ„๋‹จํ•˜๊ฒŒ ์ดํ•ดํ•ด๋ณด์ž!

public class PostController{
    @GetMapping("/post/{id}")
    public String postDetail(@PathVariable long id, Model model) {
        Post currentPost = postService.findPostById(id);
        model.addAttribute("post", currentPost);
        return "post/detail";
    }
}

public class PostService{
    public Post findPostById(long id) {
        // findById์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์ด Post๋ผ๊ณ  ๊ฐ€์ •
        return postRepository.findById(id);
    }
}

๋งŒ์•ฝ ์กด์žฌํ•˜์ง€ ์•Š๋Š” id๊ฐ€ ๋“ค์–ด์™”์„ ๊ฒฝ์šฐ currentPost์—๋Š” null๊ฐ’์ด ์ €์žฅ๋œ๋‹ค.
์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ ๋ฐฉ์–ด ๋กœ์ง ์—†์ด ์ฝ”๋“œ๊ฐ€ ๋Œ์•„๊ฐ€๋ฉด detail.html์—์„œ ์—๋Ÿฌ ๊ตฌ๋ฌธ์„ ๋ณด๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.
์ฆ‰, ์ด๋Ÿฐ ์ƒํ™ฉ์—์„œ Optional์„ ์ด์šฉํ•˜๋ฉด null-safetyํ•œ ์ฝ”๋“œ๋ฅผ ์งค ์ˆ˜ ์žˆ๋‹ค.

๐Ÿ’ธ Optional์˜ ๋น„์šฉ

Optional์— ๋Œ€ํ•œ ์„ค๋ช…์„ ๋ณผ ๋•Œ๋งˆ๋‹ค ๋น„์šฉ์ด ๋น„์‹ธ๋‹ค๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
์šฐ์„  Optional์„ ์ด์šฉํ•œ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ค ๊ฒƒ์ด ์žˆ๋Š”์ง€ ๊ฐ„๋‹จํ•˜๊ฒŒ ํ™•์ธํ•ด๋ณด์ž!

public class BoardService{
    public Post findByPostId(long id) {
        // findById์˜ ๋ฐ˜ํ™˜ํƒ€์ž…์ด Post๋ผ๊ณ  ๊ฐ€์ •
        Post currentPost = postRepository.findById(id);
        Optional<Post> optPost = Optional.of(currentPost);
        
        // ์˜ˆ์‹œ๋ฅผ ์œ„ํ•œ .get() ์‚ฌ์šฉ
        return optPost.get();
    }
}

๊ณต๊ฐ„์  ๋น„์šฉ

Optional<Post> optPost = Optional.of(currentPost);
  • ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ Optional์€ ๊ฐ์ฒด๋ฅผ ๊ฐ์‹ธ๋Š” ํ˜•ํƒœ๋ฅผ ํ•˜๊ณ  ์žˆ๋‹ค.
  • ์ฆ‰, ๊ธฐ์กด ๊ฐ์ฒด์— ๋Œ€ํ•œ ๋ฉ”๋ชจ๋ฆฌ๋Š” ๋ฌผ๋ก ์ด๊ณ , Optional ๊ฐ์ฒด๋ฅผ ์ €์žฅํ•˜๊ธฐ ์œ„ํ•œ ์ถ”๊ฐ€ ๊ณต๊ฐ„์ด ํ•„์š”ํ•˜๋‹ค.

์‹œ๊ฐ„์  ๋น„์šฉ

return postRepository.findById(id);

๊ฐ€์žฅ ์ด์ƒ์ ์ธ ๋ฐฉ์‹์€ ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ Repository๋ฅผ ํ†ตํ•ด ์ฐพ์€ ๊ฐ์ฒด๋ฅผ ๋ฐ”๋กœ ๋ฐ˜ํ™˜ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.
ํ•˜์ง€๋งŒ ๊ฐ’์ด 100% ๋“ค์–ด์žˆ๋‹ค๊ณ  ๊ฐ€์ •ํ•  ์ˆ˜ ์—†๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— Optional๋กœ ๊ฐ์‹ธ์„œ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

// ๋“ค์–ด์žˆ๋Š” ๊ฐ’์ด null์ด๋ฉด NoSuchElementException ๋ฐœ์ƒ
return optPost.get();
  • ์œ„ ์ฝ”๋“œ์ฒ˜๋Ÿผ optPost์— ๋“ค์–ด์žˆ๋Š” ๊ฐ์ฒด๊ฐ€ null์ธ์ง€ ๋น„๊ตํ•˜๊ณ , ๊บผ๋‚ด์˜ค๋Š” ๊ณผ์ •์„ ๊ฑฐ์นœ๋‹ค.
  • ์ฆ‰, Post ๊ฐ์ฒด๋ฅผ ์–ป๊ธฐ ์œ„ํ•ด์„œ .get() ๋ฉ”์†Œ๋“œ๋ฅผ ๊ฑฐ์ณ ๋ฐ˜ํ™˜๋ฐ›๊ธฐ ๋•Œ๋ฌธ์— ์‹œ๊ฐ„์  ๋น„์šฉ์ด ๋“ ๋‹ค.
    • .get()์˜ ๊ฒฝ์šฐ ๋‹ค๋ฅธ ๋ฉ”์†Œ๋“œ์— ๋น„ํ•ด ์˜ค๋ž˜ ๊ฑธ๋ฆฌ๋Š” ํŽธ์€ ์•„๋‹ˆ๋‹ค.

๐Ÿ›  Optional ์‚ฌ์šฉํ•˜๊ธฐ

๐Ÿšจ ์ฃผ์˜ ์‚ฌํ•ญ

  • ๋˜๋„๋ก์ด๋ฉด ๋ฐ•์‹ฑ๋œ ๊ธฐ๋ณธ ํƒ€์ž…์„ ๋‹ด์€ ์˜ต์…”๋„์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ผ์€ ์—†๋„๋ก ํ•˜์ž!
  • Optional์„ ๋ฐ˜ํ™˜ํ•ด์•ผํ•œ๋‹ค๋ฉด ์ ˆ๋Œ€ null์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ๋ง์ž!
    • Optional์„ ๋„์ž…ํ•œ ์ทจ์ง€๋ฅผ ์™„์ „ํžˆ ๋ฌด์‹œํ•˜๋Š” ํ–‰์œ„์ด๋‹ค.

โš™๏ธ Default Value

null์ด ๋ฐ˜ํ™˜๋˜์ง€ ์•Š๋„๋ก ๊ธฐ๋ณธ๊ฐ’์„ ์ •ํ•ด ์‚ฌ์šฉํ•˜์ž.

/* ์•„๋ž˜ ์ฝ”๋“œ๋Š” ๋‹จ์ˆœํžˆ ์„ค๋ช…์„ ์œ„ํ•œ ์˜ˆ์‹œ ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค. */
public class PostService{
    public Post findPostById(long id) {
        Optional<Post> findPost = postRepository.findById(id);
        return findPost.orElse(findRandomPost());
    }
    
    public Post findRandomPost(){
        Post randomPost = ...;
        return randomPost;
    }
}

์œ„ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด orElse(Object)๋ผ๋Š” ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•œ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

  • findPost์—์„œ ๊บผ๋‚ด์˜จ ๊ฐ’์ด null์ผ ๋•Œ, ๊ธฐ๋ณธ ๊ฐ’์„ ์ •ํ•ด ๋ฐ˜ํ™˜ํ•œ๋‹ค.
    • ์ฆ‰, ์œ„ ์ฝ”๋“œ์—์„œ๋Š” ๋žœ๋ค ๊ฒŒ์‹œ๊ธ€์„ ๊ธฐ๋ณธ ๊ฐ’์œผ๋กœ ์ง€์ •ํ•ด ์‚ฌ์šฉํ•œ ๊ฒƒ์ด๋‹ค.

โ˜„๏ธ Throw Exception

์ƒํ™ฉ์— ๋งž๋Š” ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ null์„ ๋Œ€๋น„ํ•˜์ž.

public class PostService{
    public Post findPostById(long id) {
        Optional<Post> findPost = postRepository.findById(id);
        return findPost.orElseThrow(RuntimeException::new);
    }
}

orElse()๊ฐ€ ๊ธฐ๋ณธ ๊ฐ’์„ ์ง€์ •ํ•˜๋Š” ๊ฒƒ์ด๋ผ๋ฉด, orElseThrow()๋Š” ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ๊ฒƒ์ด๋‹ค.

  • findPost์—์„œ ๊บผ๋‚ด์˜จ ๊ฐ’์ด null์ผ ๋•Œ, RuntimeException์„ ๋ฐœ์ƒ์‹œํ‚จ๋‹ค.

์—ฌ๊ธฐ์„œ Spring์˜ ํ•ธ๋“ค๋ง์„ ์ด์šฉํ•˜๋ฉด ๋” ์ข‹์€ ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋‹ค.

// Custom Exception
public class PostIdException extends RuntimeException{
    public PostIdException(String msg){
        super(msg);
    }
}

// Exception Handler
@ControllerAdvice
@Slf4j
public class ExceptionHandlerController { 
    @ExceptionHandler(value = PostIdException.class) 
    public @ResponseBody String notExistId(PostIdException e){
        log.error("PostIdException={}", e);
        return Script.href("/",e.getMessage());
    }
}

public class PostService{
    public Post findPostById(long id) {
        Optional<Post> findPost = postRepository.findById(id);
        return findPost.orElseThrow( () -> new IdNotFoundException(id + "๋ฒˆ ๊ฒŒ์‹œ๋ฌผ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."));
    }
}

์œ„์™€ ๊ฐ™์ด IdNotFoundException์ด๋ผ๋Š” Custom Exception์„ ๋งŒ๋“ค์—ˆ๋‹ค.
๋งŒ์•ฝ findPost๊ฐ€ null์ผ ๊ฒฝ์šฐ IdNotFoundException์ด ๋ฐœ์ƒ๋˜๊ณ , ExceptionHandlerController๋ฅผ ํ†ตํ•ด notExistId() ๋ฉ”์†Œ๋“œ๊ฐ€ ์‹คํ–‰๋œ๋‹ค.

์˜ˆ์™ธ๋ฅผ ๋˜์ง€๊ธฐ๋งŒ ํ•˜๋Š” ๊ฒƒ๋ณด๋‹จ, ํ•ด๋‹น ์˜ˆ์™ธ๊ฐ€ ๋ฐœ์ƒํ–ˆ์„ ๋•Œ ์ฒ˜๋ฆฌ๋ฅผ ํ•ด์ฃผ๋Š” ๊ฒƒ์ด ํ›จ์”ฌ ์•ˆ์ •์ ์ด๋‹ค.

๐Ÿคจ Exist..?

Optional์€ ๋Œ€๋ถ€๋ถ„ ์œ„์— ์†Œ๊ฐœํ•œ ๊ธฐ๋Šฅ๋“ค๋กœ ์ถฉ๋ถ„ํžˆ ๊ฐ์ฒด๋ฅผ ๊บผ๋‚ด๊ฑฐ๋‚˜ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค.
ํ•˜์ง€๋งŒ ์ ํ•ฉํ•œ ๋ฉ”์†Œ๋“œ๋ฅผ ์ฐพ์ง€ ๋ชปํ–ˆ๋‹ค๋ฉด ์•„๋ž˜ ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹๋‹ค.
๋‹จ, ์•ž์„œ ์†Œ๊ฐœํ•œ ๋ฉ”์†Œ๋“œ๋กœ ๋Œ€๋ถ€๋ถ„ ๋Œ€์ฒดํ•  ์ˆ˜ ์žˆ์œผ๋‹ˆ ์ตœ๋Œ€ํ•œ ํ”ผํ•˜๋„๋ก ํ•˜์ž!

isPresent(), isEmpty()

์ด๋ฆ„๊ณผ ๊ฐ™์ด Optional ๊ฐ์ฒด์— ๊ฐ’์ด ๋“ค์–ด์žˆ๋Š”์ง€, ๋น„์–ด์žˆ๋Š”์ง€(null) ํ™•์ธํ•˜๋Š” ๋ฉ”์†Œ๋“œ์ด๋‹ค.

public class PostService{
    public Post findPostById(long id) {
        Optional<Post> findPost = postRepository.findById(id);
        
        // ๊ฐ ๋ฉ”์†Œ๋“œ์˜ ์‚ฌ์šฉ ์˜ˆ์‹œ๋ฅผ ์œ„ํ•ด if, else-if ํ˜•ํƒœ๋กœ ์ž‘์„ฑ
        if (findPost.isPresent()) {
            return findPost.get();
        } else if(findPost.isEmpty()) {
            throw new IdNotFoundException("id" + "๋ฒˆ ๊ฒŒ์‹œ๋ฌผ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
        }
        
        return null;
    }
}

์šฐ๋ฆฌ๋Š” ์ด ์ฝ”๋“œ๋ฅผ ์•„๋ž˜๋กœ ๋ฐ”๊ฟ” ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

public class PostService{
    public Post findPostById(long id) {
        Optional<Post> findPost = postRepository.findById(id);
        return findPost.orElseThrow( () -> new IdNotFoundException(id + "๋ฒˆ ๊ฒŒ์‹œ๋ฌผ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค."));
    }
}

์ด๋ ‡๊ฒŒ isEmpty(), isPresent()๋Š” ๋‹ค๋ฅธ ๋ฉ”์†Œ๋“œ๋กœ ์ถฉ๋ถ„ํžˆ ๋Œ€์ฒดํ•ด ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

ifPresent(), ifPresentOrElse()

๊ฐ์ฒด๋ฅผ ๋ฐ˜ํ™˜๋ฐ›์ง€ ์•Š๊ณ , ๊ฐ’์ด ์กด์žฌํ•  ๋•Œ ๋กœ์ง์„ ์‹คํ–‰ํ•ด์•ผํ•  ๋•Œ๋„ ์žˆ๋‹ค.
์•„๋ž˜ ์˜ˆ์‹œ๋Š” ๋“ค์–ด์˜จ id๋ฅผ ๊ฐ€์ง„ Post ๊ฐ์ฒด๊ฐ€ ์ €์žฅ์†Œ์— ์กด์žฌํ•œ๋‹ค๋ฉด ํ•ด๋‹น ๊ฐ์ฒด๋ฅผ ์ˆ˜์ •ํ•˜๊ณ , ๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด ์˜ˆ์™ธ๋ฅผ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์ฝ”๋“œ์ด๋‹ค.

public class PostService{
    
    public boolean isPostOwner(String ownerName, String loginName) {
        return ownerName.equals(loginName);
    }
    
    public void updatePost(long id, PostDto postDto, Member member) {
        Optional<Post> optPost = Optional.of(postRepository.findById(id));
        optPost.ifPresentOrElse(post -> {
            if (isPostOwner(post.getMember().getUsername(), member.getUsername())) {
                post = modelMapper.map(postDto, Post.class);
                postRepository.save(post);
            }}, () -> {
                throw new IdNotFoundException(id + "๋ฒˆ ๊ฒŒ์‹œ๋ฌผ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
        });
    }
}

์ฝ”๋“œ๋ฅผ ์กฐ๊ธˆ ๋” ์งค๋ผ์„œ ํ™•์ธํ•ด๋ณด๋„๋ก ํ•˜์ž!

optPost.ifPresentOrElse(post -> {
    if (isPostOwner(post.getMember().getUsername(), member.getUsername())) {
        post = modelMapper.map(postDto, Post.class);
        postRepository.save(post);
    }
}, // comma๋ฅผ ๊ธฐ์ค€์œผ๋กœ ๊ฐ’์ด ์žˆ์„ ๋•Œ์™€ ์—†์„ ๋•Œ๋กœ ๋‚˜๋‰œ๋‹ค.
    () -> {
        throw new IdNotFoundException(id + "๋ฒˆ ๊ฒŒ์‹œ๋ฌผ์€ ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
});

optPost์— ๊ฐ’์ด ์กด์žฌํ•œ๋‹ค๋ฉด post -> { if(...) } ๋ถ€๋ถ„์ด ์‹คํ–‰๋˜๊ณ , null์ด๋ผ๋ฉด () -> {throw ...}์ด ์‹คํ–‰๋œ๋‹ค.

๐Ÿค” ํšŒ๊ณ 

Optional์ด ์ œ๊ณตํ•˜๋Š” ๋ฉ”์†Œ๋“œ์— ๋Œ€ํ•ด ์•Œ๊ธฐ ์ „๊นŒ์ง€๋Š” ํ•ญ์ƒ .get()์„ ์ด์šฉํ•ด ๊ฐ’์„ ๊บผ๋‚ด์˜ค๊ณคํ–ˆ๋‹ค.

์ด๋ฒˆ ์ •๋ฆฌ๋ฅผ ํ†ตํ•ด์„œ Optional์„ ์•ž์œผ๋กœ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•ด์•ผ ์ข‹์„์ง€ ๊ณ ๋ฏผํ•  ์ˆ˜ ์žˆ๋Š” ์ข‹์€ ๊ณ„๊ธฐ๊ฐ€ ๋˜์—ˆ๋‹ค.

๋ ˆํผ๋Ÿฐ์Šค

ํƒœ๊ทธ: ,

์นดํ…Œ๊ณ ๋ฆฌ:

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ