[Springboot] 18 블로그 만들기 V3 계속 (findById를 만들어야 하는 이유 - 조인)

조인을 하는경우 myFindById 를 만들어 쓰자!
김호정's avatar
Sep 06, 2024
[Springboot] 18 블로그 만들기 V3 계속 (findById를 만들어야 하는 이유 - 조인)
 
 
//Pageable pg = PageRequest.of(0, 3, Sort.Direction.DESC, "id");
페이징하는 코드
 
notion image
이렇게 커스터마이징해서 FindAll 매서드를 직접 만들어서 써도 된다.
  • m을 붙이는건 컨벤션 : )
페이징할 때 → 맨끝에 limit 을 붙여줘라. = 오라클 오프셋
 
++
네임메서드? 공부하지 말고 ( 지금은 헷갈림 ) @Query 해서 만들어 쓰기
 
++
Java에서 Optional 객체를 만드는 방법은 여러 가지가 있습니다. Optional은 Java 8에서 추가된 클래스이며, 이는 값이 있을 수도 있고 없을 수도 있는 컨테이너 역할을 합니다. Optional을 사용하면 null 값을 다룰 때 발생할 수 있는 NullPointerException을 방지할 수 있습니다.다음은 Optional 객체를 만드는 몇 가지 방법입니다.1. 빈 Optional 객체 생성빈 Optional 객체를 만들려면 Optional.empty()를 사용합니다.
java Optional<String> emptyOptional = Optional.empty();
2. 값이 있는 Optional 객체 생성값이 있는 Optional 객체를 만들려면 Optional.of()를 사용합니다. 이 메서드는 절대 null을 허용하지 않습니다. 만약 null을 전달하면 NullPointerException이 발생합니다.
java Optional<String> optionalWithValue = Optional.of("Hello, World!");
3. null이 될 수 있는 값을 가진 Optional 객체 생성null이 될 수 있는 값을 감싸는 Optional을 만들려면 Optional.ofNullable()을 사용합니다. 이 메서드는 null 값을 허용하며, 만약 전달된 값이 null이면 빈 Optional 객체를 반환합니다.
java Optional<String> optionalWithNullableValue = Optional.ofNullable("Hello, World!"); Optional<String> optionalWithNull = Optional.ofNullable(null); //Optional 반환
4. Optional 값 사용하기Optional 객체에서 값을 가져오는 방법은 여러 가지가 있습니다. 예를 들어, 값이 존재할 때만 특정 작업을 수행하려면 ifPresent() 메서드를 사용할 수 있습니다.
java optionalWithValue.ifPresent(value -> System.out.println(value));
값이 있으면 그 값을 반환하고, 없으면 기본값을 반환하려면 orElse() 메서드를 사용할 수 있습니다.
java String value = optionalWithValue.orElse("Default Value");
이와 같은 방법으로 Optional을 활용하면 null을 보다 안전하게 처리할 수 있습니다.

UserService

notion image
 
회원가입할 때 findByUsername 타입을 repository에서 User로 설정하면
나중에 여기 UserService에서 받아서 null 처리를 직접해야한다. 조건문으로
 
근데 까먹고 null 처리를 안 할 위험이 있다.
 
그래서 애초에 repository에서 Optional<User> 타입으로 설정해서
존재하는 경우 isPresent ( → 회원가입은 존재하는게 예외이니까 ) throw를 날려준다.
 
Optional이 null이 아닌 경우 throw를 날려주고 이제 남은 null인 경우에는 save 해준다 : )
 
테스트해보자.
notion image
 
NoSuchElementException 에러 난다.
 
 
작성한 코드에서 발생하는 에러의 원인은 Optional.ofNullable(user)에서 usernull인 경우, op.get() 메소드를 호출하면 NoSuchElementException이 발생하기 때문입니다.
Optional.get() 메소드는 Optional 객체 안에 값이 있을 때만 그 값을 반환하며, 값이 없을 때 (Optional.empty()) 호출하면 NoSuchElementException을 던집니다. Optional.ofNullable(user)에서 usernull이면 Optional.empty()가 되므로, 그 상태에서 get()을 호출하면 에러가 발생하는 것입니다.
에러를 방지하려면, Optional 안에 값이 있는지 먼저 확인한 다음에 값을 처리해야 합니다. 이를 위한 방법 중 하나는 isPresent()를 사용하거나, orElse()orElseThrow() 같은 메소드를 사용하는 것입니다.
코드를 수정한 예시는 다음과 같습니다:

1. isPresent()로 값이 있는지 확인

java 코드 복사 @Test public void go() { User user = null; Optional<User> op = Optional.ofNullable(user); if (op.isPresent()) { User userPS = op.get(); System.out.println(userPS.getId()); } else { System.out.println("User is null"); } }

2. orElse()로 기본값 제공

java 코드 복사 @Test public void go() { User user = null; Optional<User> op = Optional.ofNullable(user); User userPS = op.orElse(new User()); // user가 null일 때 기본 User 객체 사용 System.out.println(userPS.getId()); }

3. orElseThrow()로 예외 처리

java 코드 복사 @Test public void go() { User user = null; Optional<User> op = Optional.ofNullable(user); User userPS = op.orElseThrow(() -> new IllegalArgumentException("User is null")); System.out.println(userPS.getId()); }
이 중 적절한 방법을 선택하여 null일 때 어떻게 처리할지에 따라 코드를 작성하면 됩니다.
 
notion image
isPresent로 값이 있는지 확인하고 null인경우 throw 해서 RuntimeException 을 발동시킴.
 
notion image
OrElse 로 처리할 수도 있다. ( → 만약 빈 유저객체가 리턴되었으면, user.builder().username(”ssar”)
이런식으로 만약에 값이 없으면 service레이어 에서 객체를 만들어서 넣어주겠다.! 할 때 사용한다.
“없으면 내가 객체를 강제로 만들어서 리턴하겠다” )
→ 상황에 따라서 orElse를 사용함.
 
notion image
orElseThrow를 많이 사용한다. → 있으면 꺼내고 없으면 throw 날리는 거 !
작성한 RuntimeException이 잘 터졌다.
 
@Test public void go(){ User user = null; Optional<User> op = Optional.ofNullable(user); // User userPS = op.get(); // System.out.println(userPS.getId()); // 1 // if(op.isPresent()){ // User userPS = op.get(); // }else{ // throw new RuntimeException("존재하지 않아요"); // } // 2 // User u = User.builder().id(1).username("ssar").build(); // User u2 = op.orElse(u); // System.out.println(u2.getUsername()); // 3 User u = op.orElseThrow(() -> new RuntimeException("fdsafdsafdsa")); // 4 }
 
여기부터는 약간 이해못했지만 일단 이해하려고 노력해보기.
 
  • 내부에 들어오는 타입이 Supplier (인터페이스)인데 인터페이스이니까 new 할 수 없어서
    • 인터페이스를 넣어야하면 익명객체(익명클래스)를 만들어서 넣어야 하는데
      new Callable해서 적을수는 없으니 ? new Supplier 해서 적으면 지저분해지니 ?
      짧게 적을때 아래와 같이 람다식으로 적을 수 있다.
       
notion image
→ 자바는 여기 인터페이스를 new 할 수 없으니 람다식을 사용할 수 있게 해줌
 
!) 자바에서는 함수를 파라미터로 전달 하지 못한다! ( → 1급 객체가 아니니까! )
1급 객체가 아닌거는 함수의 매개변수로 전달하지 못한다.
자바에서 매개변수로 전달할 수 있는건 기본적으로 Class 타입!
 
자바에서 orElseThrow 다음에 원하는건 “행위”
자바가 아니었으면 function 이 들어갈 자리. (자바스크립트였으면)
근데 자바는 행위(≠1급객체)를 전달하지 못한다. 그래서 어쩔 수없이 인터페이스를 만들어쓴다.
근데 인터페이스는 함수의 몸체가 없다. (추상화로 만듦)
그래서 여기 인터페이스를 넣어줘야 하는데 인터페이스는 new를 못하니까
익명 함수(익명 클래스)로 만들어서 넣어줘야 한다.
익명 클래스로 만들어서 코드를 길~게 적을 바에
인터페이스를 넣어야하는 자리에는 람다식을 사용!!!
 
() → 여기 들어가는게 행위! (→ Supplier 인터페이스의 get 매서드같은거 )
 
행위를 넣는 자리에 람다식을 쓸 수 있다.
 
 
  • Supplier 인터페이스
    • Supplier 라는 인터페이스를 implements 해서 get 매서드를 사용할 수 있다.
notion image
  • Callable 타입
 
 
게시글 수정화면 매서드의 경우도
notion image
 
  • Optional로 받아서 null인 경우 isEmpty 처리해주고 op.get해서 권한처리를 할 수도 있지만
  • 객체로 받아서 orElseThrow 해주는게 코드가 더 깔끔하고 간결하다.
 
게시글상세보기 findById 매서드
notion image
Optional을 쓰기보다 orElseTrow 를 해서 엔티티로 받으라고 했는데
위처럼 1건만 찾을 때는 옵셔널을 쓰는게 낫다.
(→ board 테이블이랑 해당 게시글 작성자 user 테이블 조인해서 들고오는 매서드 )
 
대표적인 데이터베이스 프레임워크 2개
 
  1. JPA
  1. MyBatis
 
++
익명객체(익명클래스)
 
 
 
게시글 상세보기를 위해 mFindById을 만든 이유는 조인을 해야하니까!
( 조인을 왜 함 ? 레이지 로딩을 안 하게 할려고 미리 username을 들고 옴.
상세보기 페이지에서 username이 필요 )
 
게시글 수정하기, 삭제하기 할때는 존재하는지 확인하려고 findById 를 쓴거임!
이런경우 mFindById를 쓰면 안됨. ( 조인은 연산을 많이함 )
 
그리고
 
driving 테이블 - fk - n
driven 테이블
 
유일하지 않으면 계속 찾아야하니까 엔포드를 사용하는 것이다.
(→ 어느 테이블을 select 할지 생각하고 쿼리를 작성해야 한다.)
어느테이블에 어느테이블을 조인시킬지,,
 
유일하지 않은 것을 빨리찾는방법은 모아두는 것!
클러스터링 (김씨는 강원도에 모아둬)
 
데이터를 서치하는건 오래 걸리기 때문에
내가 찾아야하는 데이터가 잘 정렬이 되어있거나 유일한게 좋음
 
notion image
조인은 내 테이블을 먼저 찾고 조인할 테이블 행을 다 비교하면서 찾음.
→ on절은 2 테이블을 서로 비교하면서 연산함
→ fk랑 pk랑 동일한게 있는지 찾음
→ 근데 이 연산은 오래걸림
→ 근데 더 오래걸리는게 select 2번 하는거임
 
조인이 아무리 복잡하더라도 속도가 io보다 느릴수는 없다.
그러니 select 를 2번 하는것보다 차라리 조인을 쓰는게 낫다.
 
select는 그럼 언제 2번 해야할까?
→ 코드를 짤 때 가독성이 좋아지는 때가 있다.
코드가 읽기 쉬워지니까 select 2번 하는 경우도 있다.
빠른게 무조건 중요한게 아니라 가독성도 중요하다.
 
 
notion image
서비스의 게시글상세보기를 findById로 하면 레이지 로딩이 발생함
( username 또 가져와야 하니까 1번 더 select 함 )
그래서 mFindById를 만들어서 조인을 사용해 select 가 1번만 일어나도록 수정함
 
notion image
 
조인을 해서 select 가 1번 되는 걸 확인 할 수 있다.
 
notion image
 
쿼리는 이렇게 썼다.
 
 
Share article

keepgoing