[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
์ ์์ผ๋ก ์ด๋ป๊ฒ ํ์ฉํด์ผ ์ข์์ง ๊ณ ๋ฏผํ ์ ์๋ ์ข์ ๊ณ๊ธฐ๊ฐ ๋์๋ค.
๋ ํผ๋ฐ์ค
- Effective Java 3/E Item. 55 - ์ต์ ๋ ๋ฐํ์ ์ ์คํ ํ๋ผ
- ์ ์ง์ฐ๋ ๋ธ๋ก๊ทธ
- ๊นํ์๋ ๋ธ๋ก๊ทธ
๋๊ธ๋จ๊ธฐ๊ธฐ