Loading...

Spring/Blog-V1 / / 2022. 3. 4. 17:27

스프링 21강. UserRepository(userEntity)

반응형

 

지금까지의 상황은 아래 그림과 같다.

이제 Repository를 만들어보자.

 

지금부터 Repository 만드는 과정은 문법! 지켜야 한다!!

 

Repository는 class가 아닌 interface이다.

그리고 JpaRepository를 상속받아야 한다.

 

JpaRepository는 제네릭으로 타입을 결정한다.

 

네가 관리하는 오브젝트가 어떤 타입인지 알려줘

그 테이블의 PK 타입을 알려줘

 

JpaRepository를 extends 받고 첫 번째 제네릭에 User라고 적어만 주면

User 오브젝트를 말한다는 것을 알고

오브젝트에 대한 모든 정보를 가져와서

JpaRepository가 미리 만들어놓은 메서드를 사용할 수 있게 해 준다.

 

단순하고 대표적인 쿼리는 미리 제공해준다.

JOIN과 같은 복잡한 연산은 제공하지 않는다.

 

복잡한 연산은 직접 만들어야 한다!

 

Repository를 IoC 컨테이너에 올리기 위해 @Repository 어노테이션을 붙여준다.

 

사실 이 어노테이션은 붙여주지 않아도 메모리에 뜬다.

부모 클래스에 이미 정의되어 있기 때문이다!

 

그리고 JpaRepository가 User 오브젝트에 맞게 다 파싱 해준다!!

엄청 편하다 ㅜㅜ

 

package site.metacoding.dbproject.domain.user;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository // 사실 안붙여도 됨. 부모 클래스에 정의되어 있기 때문.
public interface UserRepository extends JpaRepository<User, Integer> {

}

 


 

메모리에 UR이 뜨자마자

무조건 영속성 컨텍스트와 연결된다.

 

여기까지는 UserController와 UserRepository가 연결되어 있지 않다.

연결시켜주자.

 

컴포넌트 스캔을 하고

@Controller 어노테이션이 붙은 UC를 메모리에 띄우려는데

매개변수를 넣어줘야 new가 가능한 상황이다.

 

이때 매개변수로 UR이 필요한데

이 UR은 IoC 컨테이너에서 서칭 한다.

 

UR이 IoC에 있으면 DI에 성공이고,

없으면 실패한다.

 

UR에 @Repository를 붙여주기도 했고

JpaRepository를 상속하고 있기도 하니

메모리에 띄워져 있다.

 

UC에서 UR이 필요하니까

UC에서 의존성을 주입받는다.

 

UC에서 UR을 마음대로 new 할까 봐

UR을 인터페이스로 만든 것이다.

 

만약 class로 만들었다면 아무데서나 new가 가능하겠지만

class로 만들면 JpaRepository가 상속이 불가능하다.

 

완전히 강제적으로 만들어놓은 것이다.

 

DI가 가능하려면 반드시 IoC 컨테이너에 떠있어야 한다!!

 


// 컴포지션(의존성 연결) : 컨트롤러는 레파지토리에 의존해야해!
private UserRepository userRepository;

// DI 받는 코드!!
public UserController(UserRepository userRepository) {
    this.userRepository = userRepository;
}

final이 붙은 애들을 생성자로 받아오게 하는 어노테이션

@RequiredArgsConstructor

 

final이 붙으면 read only, 상수가 된다.

상수는 반드시 선언과 동시에 초기화가 되어야 한다.

 

userRepository를 미리 초기화해둘 수없다.

생성자를 통해 의존성을 주입받아야 하기 때문이다.

 

그래서 @RequiredArgsConstructor를 붙여주면

생성자를 통해 초기화될거야 걱정마! 라고 알려주는 것이기 때문에

오류가 나지 않는다.

 

이를 사용하면 일일이 생성자에 추가해두지 않아도

편하게 사용이 가능하다!

 

지금은 개념을 이해해야 하니까

생성자에 직접 주입받는 코드로 작성하자.

 

@Controller
public class UserController {

    // 컴포지션(의존성 연결) : 컨트롤러는 레파지토리에 의존해야해!
    private UserRepository userRepository;
    private PostRepository postRepository;

    // DI 받는 코드!!
    public UserController(UserRepository userRepository, PostRepository postRepository) {
        this.userRepository = userRepository;
        this.postRepository = postRepository;
    }
}

@Controller
@RequiredArgsConstructor
public class UserController {

    // 컴포지션(의존성 연결) : 컨트롤러는 레파지토리에 의존해야해!
    private final UserRepository userRepository;
    private final PostRepository postRepository;
}

 


 

이제 UC와 UR이 연결되었다.

UC에서 INSERT 요청을 해볼 것이다.

 

회원가입 join 메서드에서 우리가 요청할 쿼리는

username=ssar&password=1234&email=ssar@nate.com이다.

x-www-form-urlencoded 타입이다.

 

파싱을 위해 join 메서드의 매개변수로

String username, String password, String email을 일일이 적어주지 않아도

User user만으로 오브젝트를 받아올 수 있다.

맙소사!

 

받아온 user 오브젝트를

userRepository.save( ) 메서드 안에 넣어서 보내주면

디비에 INSERT가 가능하다.

 

save( )는 JpaRepository의 INSERT 메서드이다.

// 기본적인 쿼리는 구현되어 있고, 그 중 많이 쓰는 4가지!

// SELECT * FROM user;
findAll();

// SELECT * FROM user WHERE id = ?
findById();

// INSERT INTO user(username, password, email, createDate) VALUES(?, ?, ? , ?)
save();

// DELETE FROM user WHERE id = ?
deleteById();

// update는 없어요!! - 영속성 컨텍스트 공부하면 사용할 수 있음

createDated에 자동으로 현재 시간을 넣을 수 있다.

 

User 클래스 위에

@EntityListeners(AuditingEntityListener.class) 어노테이션을 붙여준다.

엔티티가 만들어지는지 지켜보는 리스너이다.

 

그리고 createDate위에 @CreatedDate

updateDate 위에 @LastModifiedDate

이 두 개 코드는 모든 Entity에 있어야 한다.

 

처음 insert 할 때 createDate, updateDate 둘 다 현재 시간 값이 들어가고,

update를 할 때는 updateDate만 수정된다.

 

LocalDateTime을 사용하면 로컬을 기준으로 시간이 적용된다.

 

로컬은 누구를 말하는 것일까?

 

다른 나라에 있는 사용자 두 명이 서버 입장에서 동시에 접근을 했다면

호주 기준 시간을 적용할까, 필리핀 기준 시간을 적용할까?

 

모두 아닌 서버 입장에서 시간을 계산한다.

서버가 기준인 시간 타입이 LocalDateTime이다.

 

만약 서버를 중국에 두었다면 OS의 시간을 한국시간으로 변경하고 서비스해야 한다.

 

즉 로컬은 OS의 시간이 기준이라는 말이다.

 

마지막으로 DbprojectApplication.java 파일에 @EnableJpaAuditing 어노테이션을 걸어준다.

 

JpaAuditing을 활성화시켜주는 것이다.

 


 

바디에서 전송한 데이터는 DB에 들어가고

그렇지 않은 컬럼에는 null이 들어가게 된다.

 

save로 보낸 user 오브젝트를 Repository를 타고

영속성 컨텍스트까지 가지고 온다.

 

여기서 Repository와 DB 사이의 중간 모듈의 역할해주는

영속성 컨텍스트가 알아서 쿼리(INSERT)까지 만들어서

DB로 flush 해준다.

 

영속성 컨텍스트에서 DB로 보내는 user 오브젝트에는

id값은 null인 채로 보내졌다.

INSERT 하는 순간 createDate와 updateDate에

현재 시간 값이 들어갈 것이다.

 

DB에 INSERT 하여 1행이 추가되면

다시 영속성 context로 리턴해주는데

DB에서 리턴해준 데이터와

원래 영속성 컨텍스트가 가지고 있던

user 오브젝트가 동기화된다. 

 

이때 DB와 동기화되어 auto_increment로 id가 추가되고,

동기화 된 객체를 Entity라고 한다.

 

스프링에서 Entity는 그냥 테이블의 다른 말, 그냥 오브젝트가 아닌

DB 데이터가 동기화 된 자바 객체를 말한다.

 

 


 

INSERT 해보자!

회원가입 버튼을 누른 후 성공적으로 로그인하라는 화면이 나온다면

데이터베이스 user 테이블에 데이터가 잘 들어왔는지 확인해준다.

성공!

 

DB에 보내기 전의 user 오브젝트와

DB와 동기화 된 userEntity를 비교하기 위해

로그를 찍어보자.

// username=ssar&password=1234&email=ssar@nate.com (x-www 타입)
// 회원가입 INSERT - 인증(로그인) X
@PostMapping("/join")
public String join(User user) { // 행위, 페이지 아님
    System.out.println("user : " + user);
    User userEntity = userRepository.save(user);
    System.out.println("userEntity : " + userEntity);
    // redirect는 GetMapping 주소!! redirect:매핑주소
    return "redirect:/loginForm"; // 로그인페이지 이동해주는 컨트롤러 메서드를 재활용
}

user객체와 userEntity의 차이는

DB와 동기화되었냐, 안되었냐의 차이이다.

 

 

 

 

[출처]

https://cafe.naver.com/metacoding

 

메타코딩 : 네이버 카페

코린이들의 궁금증

cafe.naver.com


메타 코딩 유튜브
https://www.youtube.com/c/%EB%A9%94%ED%83%80%EC%BD%94%EB%94%A9

 

메타코딩

문의사항 : getinthere@naver.com 인스타그램 : https://www.instagram.com/meta4pm 깃헙 : https://github.com/codingspecialist 유료강좌 : https://www.easyupclass.com

www.youtube.com

 

 

 

 

반응형