시작하자
값을 넣고 회원가입 할 거다
join페이지가 없으니 만들어 주자
DTO (Data Transfer Object) 란?
데이터를 전송하기 위해 사용되는 객체(오브젝트) DTO는 보통 여러 개의 필드로 구성되어 있으며, 데이터를 전송하기 위한 목적으로 사용되기 때문에, 비즈니스 로직은 가지지 않는다. 단순히 데이터를 저장하고 전송하는 역할을 한다. 요청할 때만 쓰는 항아리...인 듯! * DTO는 getter/setter 매서드만 가진 클래스 * DB에서 데이터를 얻어서 Service나 Controller 등으로 보낼 때 사용 * 엔티티를 DTO 형태로 변환해서 사용
DTO : 클라이언트로부터 전송되는 데이터를 받는 통신 데이터
DTO 만들기 ★
제일 처음할 건 DTO를 만들 것. (DTO를 만드는 것은 컨트롤러의 책임 중 하나) 컨트롤러를 구현하기 전에 DTO를 먼저 만들어서 사용한다. DTO를 사용함으로써 컨트롤러는 클라이언트와 비즈니스 로직 사이에서 데이터를 전달하는 역할을 수행! DTO는 컨트롤러의 입출력 데이터를 담당하는 역할을 하기 때문에, 컨트롤러의 책임을 다 하고 나가는 데에 도움을 줄 것 DTO는 컨트롤러의 파싱을 도와준다... 정도로 생각?
board랑 user 패키지를 추가해서 BoardController와 UserController 클래스를 넣는다
join() 메소드에 HTTPServletRequest를 매개 변수로 받아야 하잖아? (파싱 하려고) 매개변수에 String username, String password, String email 이렇게 적을 수도 있지만, DTO를 사용하자! 항아리에 담아버리자!
UserRequest 클래스 파일을 만든다. 여기에 항아리를 만들 것! 변수를 하나씩 안적고 항아리로 받아서 관리하는게 더 편하니까! * Request(요청)할 때만 받는 데이터 항아리~ * Response(응답) 데이터는 DTO에 포함되지 않는다.
[ UserRequest ]
JoinDTO와 LoginDTO는 UserController 쪽으로 요청되는 데이터(/join과 /login)를 처리할 때 필요한 데이터를 담는다 static을 사용해서 내부 클래스에 작성하면 이름 관리하기가 편하다. (내부 클래스로 작성된 DTO는 UserController 클래스 내에서만 사용되므로, 다른 클래스와의 이름 충돌을 걱정할 필요가 없다...는 뜻.)
+) UserJoinRequestDTO 라고도 쓴다는데…
파일을 하나씩 다 쪼개가면서 쓰네?ㅜㅜ 불편한데 회사가면 이렇게 쓴다
[ 이제 매개변수를 DTO로 받아보자!! ]
static이라 . 으로 호출 가능 (UserRequest.JoinDTO requestDTO) 클래스명.메소드명 변수명
UserRequest.JoinDTO requestDTO
= String username, String password, String email
= HTTPServletRequest res
[ 회원가입 고고 ]
회원가입을 완료했더니 콘솔창에 내가 입력한 데이터가 들어오고, 로그인폼으로 리다이렉션 됐다 또한 콘솔창에 내가 회원가입 시 입력한 정보도 예쁘게 파싱되어서 들어온 것 확인! DTO가 잘 먹었네(?)
DB 처리 후 VIEW를 할 지, VIEW만 할 지 양자택일! + 컨트롤러의 책임
[ MVC 패턴 참고 블로그 ] https://youwjune.tistory.com/40 * Repository = DAO (같은 말) * DB쪽으로 접근하면 Model 이라고 부름 (지금은 그냥 Model = DAO 정도로 알자) * 자바는 비즈니스 로직만 담당
노란 박스 - DB처리 후 VEIW를 원함 (디자인 없음) 주황 박스 - VIEW만 원함 (디자인 있음)
[ 컨트롤러의 책임 ]
1. URL or URI 요청 받기 2. http body는 DTO로 받음 (항아리) / (응답은 html이나 json으로 함) 3. MIME 타입 전략 : x-www-form-urlencoded (username=ssar&password=1234) 받는다 4. (body 데이터가 있다면) 유효성 검사하기 5. 클라이언트가 view만 원하는지? 혹은 DB처리 후 VIEW(mustache)도 원하는지? 5-1. view만 원하면 view를 응답하면 끝 5-2. DB처리를 원하면 Model(DAO)에게 위임한 후, view를 응답하면 끝 이 7가지의 책임이 아닌 것들은 컨트롤러에 적으면 안된다!!
* join이 끝나면 어느 페이지로 리다이렉트 할 지를 정하는 것도 컨트롤러
(login을 하면 model에게 select 요청한 후(위임), main페이지를 리다이렉트)
* DB연결은 컨트롤러가 안 하니까 Model에 위임한다
H2 데이터베이스 연결 (yml 설정) (+JPA)
유효성 검사가 끝난 안전한 데이터를 Model에게 건네줘야 한다! 그러니 유효성 검사부터 해주자!!
아이디를 잘못 넣고 회원가입 했더니 에러 페이지가 뜬다. = 검증 완료 이제 모델에게 위임 ㄱㄱ!
[ DB 연결하기 ]
DB연결시 필요한 라이브러리들이 깔려있는지 확인! 이후 application-dev.yml에 가서 DB연결 하자!
[ JPA (Java Persistence API) 란? ] +하이버네이트
자바로 영구적으로 데이터를 기록하고 조회하는 API(=application interface 메서드의 모임)다 즉, 자바로 DB에 영구적으로 기록할 수 있는 메소드들의 모임이다 일반적으로 자바 애플리케이션에서 데이터베이스에 접근하려면 1. SELECT 요청 > DB는 ResertSet(테이블 형태의 데이터)를 리턴 2. 자바는 이 데이터를 응답 받으면 커서를 내리며 자바 코드로 파싱 3. 파싱한 데이터를 자바 오브젝트에 넣음 인데... 이런 JPA 같은 API를 넣으면 바로 [ 3. 데이터를 자바 객체에 넣음 ] 부분으로 갈 수 있다
[ 하이버네이트 ]
JPA를 구현하는 라이브러리 중 하나. 스프링 프레임워크에서는 하이버네이트를 JPA 구현체로 사용한다. 하이버네이트는 스프링이 실행될 때, 클래스들을 검색(스캔)해서 @Entity 어노테이션이 있는지 확인한다 @Entity가 붙어있는 클래스가 있으면 그 클래스를 분석한 후, 해당 테이블 생성 쿼리를 자동으로 만들어준다. 리플렉션이다! @Entitiy가 없으면 당연히 리플렉션도 안한다
나머지 2개는 JDBC요. 프로토콜이 있는 소켓 통신 그것!
[ application-dev.yml에 코드 붙여넣기 ]
spring: datasource: driver-class-name: org.h2.Driver url: jdbc:h2:mem:test;MODE=MySQL username: sa password: h2: console: enabled: true jpa: hibernate: ddl-auto: create show-sql: true properties: hibernate: format_sql: true
줄 칸 잘 맞춰서 적기!
[ 코드 설명은 이쪽 ]
서버 실행하니까 /h2-console 이라고 H2에 접속 가능한 uri가 나온다. localhost:8080/h2-console 들어가서 접속하자
url 맞추고, ID 입력한 후 들어가자!
H2 데이터베이스 연결했으면… 써보자
user_tb 테이블을 만들 것. 일단 User 클래스를 user 패키지에 생성한다. 어노테이션들을 설명해보자면 @Data = Getter, Setter, equals, hashCode, toString 등의 메서드를 자동으로 생성 @Entity = 클래스를 데이터베이스 테이블로 매핑(변환?)해주는 역할 @Entity 어노테이션은 클래스를 데이터베이스 테이블로 생성하기 위해 사용 이 어노테이션이 없으면 테이블 안 만들어짐!!! > 일단은 이정도만 이해하자 Entity는 더 배워야함 @Table(name = "user_tb") = "user_tb"라는 이름의 테이블과 매핑 @Id = 프라이머리 키 @Coulumn = 컬럼 작성하는 것인 듯? 길이는 60까지 제한두고, not null 하고. @GeneratedValue = auto-increment @CreationTimestamp = 엔티티가 생성될 때 자동으로 현재 시간을 할당하는 어노테이션
매핑 : 서로 다른 두 개의 개념이나 구조를 연결하여 데이터의 이동이나 변환을 가능
테이블 생성된 것 확인!
h2를 쓸려면 여기까지 설정이 완료되어야 함.
그래야 서버 실행될 때마다
(콘솔창에 보면) 내가 설정한 대로 테이블이 나왔단 걸 알 수 있다.
h2를 쓰는데 이렇게 테이블을 안 만들었다? > 엄청 불편할 것!
[ 제대로 만들어졌는지 H2에서 확인 ]
h2-console에 테이블이!! 만들어졌다!!
insert 넣어줬다! 그런데 이 데이터... 서버 끄면 다 날아가는거 아시죠?
Repository = DAO (+EntityManager)
Repository는 데이터베이스와 관련된 작업을 수행하는 클래스로, 일반적으로 DAO(Data Access Object)라고도 불린다. @Repository를 쓰면 스프링은 해당 클래스의 인스턴스를 자동으로 생성하고 관리 (=new 함) 지금 UserRepository라는 클래스는 내가 만든 클래스지만, IoC 컨테이너에 넣어서 사용하고 싶기 때문에 (=new를 니가 해줘) @Repository를 써줬다 필요할 때마다 내가 new를 띄워서 가져오는 것보단 IoC 컨테이너에 있는걸 넣었다 빼서 쓰는게 new도 안하고 훨씬 편하기 때문?
스프링에서 @Repository 어노테이션이 적용된 클래스는 일반적으로 기본 생성자가 필요! 그러나 매개변수가 있는 생성자만 존재하는 경우에도 스프링은 자동으로 인스턴스를 생성할 수 있다. 스프링은 @Repository 어노테이션이 붙은 클래스의 인스턴스를 관리하고 필요한 곳에 주입한다. 기본 생성자가 있는 경우에는 그대로 인스턴스를 생성하고, 매개변수가 있는 생성자만 존재하는 경우에는 해당 생성자에 필요한 매개변수를 자동으로 주입하여 인스턴스를 생성한다. (=의존 관계인데, 내가 안넣고 스프링이 알아서 의존성을 자동으로 해결해줌) 그런데 왜... EntityManager를 생성자로 주입해 준 걸까? (아래에)
@Entity는 테이블을 만들어주는 거지 new 해주는게 아니다!!
@Controller, @Repository → IoC로 제어되는 = 스프링이 new 해주는
[ EntityManager ]
데이터베이스와 상호작용하기 위해서는 JPA에서 제공하는 EntityManager를 사용해야 한다. EntityManager를 주입 받으면 UserRepository는 데이터베이스와 소통할 수 있는 능력을 갖게 된다. 즉, UserRepository가 데이터베이스와 상호작용하기 위해 필요한 도구를 받아오는게 EntitiyManager!! EntityManager와 UserRepository는 의존 관계! (= UserRepository에서 데이터베이스 기능을 사용하기 위해 EntityManager를 생성자 매개변수로 받아야 함!!) EntityManager를 필드로 받고, 생성자로 주입 (의존성 주입) 받는다. 이런 행위를 통해 EntityManager에 있는 메소드를 호출하거나 다른 기능들을 UserRepository 클래스에서 사용할 수 있는 것! (ex. UserRepository에서 데이터베이스에 사용자 정보를 저장하거나 조회하는 등의 작업을 EntityManager를 통해 수행할 수 있게 됨.)
UserRrepositoryd에 @Repository가 있으니까 new하려고 봤더니 엔티티가 있어야한다, 그래서 생성자로 주입해줌 → 의존성 주입 (Dependency Injection) EntityManager는 내가 안 띄워도 기존에 IoC에 떠있다. (JPA 라이브러리에서 제공하기 때문!) 그래서 쭉 스캔하면서 서로의(?) 짝을 찾아줌. 니가 이게 필요하네!? 하면서?!
뭔지 알겠니…? 써보자…
이렇게 계속 UserRepository가 필요할 때마다 new해서 띄울 거야? 아니지? UserRepository를 IoC 컨테이너에 넣어놓는 작업이 필요하지? @Repository 붙이러 가자!
save 메서드는 실제로 데이터베이스에 사용자 정보를 저장하는 역할을 할 것. 컨트롤러에서는 UserRequest.JoinDTO 객체를 받아와서 UserRepository의 save 메서드를 호출하기만 하면 된다. save 메서드 호출이 되는지 확인하기 위해 회원가입을 했더니
호출됐다. 만약, 값이 null이면 호출 안 된다.
UserRepository에 매개변수가 있는 생성자가 적혀 있기 때문에 UserController에서 UserRepository를 사용하기 위해서는 UserRepository를 매개변수로 받는 생성자를 정의해야 한다. UserRepository가 먼저 new가 되어야 UserController가 때릴 수 있으니까 생성자 주입을 해줘야하는 듯...?
의존성 주입 받아야 하는 것들은 모두 final 사용
final 키워드를 사용하여 필드를 선언하면 해당 필드는 반드시 초기화 되어야 한다. 따라서 의존성 주입을 받아야 하는 객체들은 final 키워드를 붙여서 선언하는 것이 좋다. (컴파일러가 해당 필드가 초기화되지 않은 상태에서 사용되는 것을 방지해주니까...)
final을 쓸 때에는 꼭 @RequiredArgsConstructor 어노테이션을 붙여주자.
@RequiredArgsConstructor는 final 상수만 생성자로 만든다!!
[ 만약 디폴트 생성자와 매개변수가 있는 생성자가 둘 다 있는 경우 ]
디폴트 생성자가 때려진다. 모르겠으면 함 해보고 결과를 봐라
[ 내가 직접 생성자 만들어주기 ]
1. 롬북의 allArgumentsContstuctor 2. final의 @RequiredArgsConstructor 3. @Autowired 어노테이션이 붙은 필드나 생성자에 해당하는 의존성을 스프링 IoC 컨테이너에서 찾아서 자동으로 주입해준다. (= 붙이면 IoC에 있는거 가져와 줌)
DB Insert 코드를 작성해보자 (2가지 방법) +@Transactional
UserRepository에서 save 메소드를 작성해준다. UserController 쪽에서 생성자 매개변수로 IoC에 띄워진 UserRepository 객체를 받았으니 save 메소드를 잘 쳐줄거다.
?! 회원가입 했더니 터졌다 ㅠㅠ TransactionRequiredException....?
이게 뭔진 잘 모르겠지만 아무튼... save 메소드 위에 @Transactional 어노테이션을 추가하니 안터짐 (org꺼를 써야함)
* @Transectional이란게 안붙으면 DB에 전송을 안함
* select할때는 @Transectinal 안붙여도 된다
H2 DB에서 조회해보면 회원 가입할 때 넣은 값이 나올거다. (확인 필요)
[ 어떤 클래스에 있는 값을 생성자를 통해서 옮길 것 ] - 2번째 방법 ㅎ…
통신으로 받은 requestDTO에 있는 정보를 User 클래스 (항아리)로 옮겨보자고요 (em = EnityManager)
User 클래스로 옮겨줄거니까... User 클래스를 enw 띄우고, user.setUsername--- 으로 항아리에 담긴 데이터들을 적용시킨다 하이버네이트 기술?? 다 적었으면 컨트롤러에서 호출해보자!
userRepository에 있는 saveV2 메소드를 호출! (requestDTO 객체를 DAO로 넘겨줘야 하니까... 아규먼츠가 requestDTO...?)
회원가입하니까 값 들어간거 콘솔창에 보이죠?
데이터베이스에도 들어왔죠?
TIP!
IoC에 있는 걸 가져다 쓰자 → 의존성 주입
@CreationTimeStamp : 자동으로 들어감
@AllargConstructor는 생성자로 만들 필요가 없는 것까지도 만든다
(단순한 int 이런 거에도 생성자를 죄다 만들어버림…)
@RequiredArgsConstructor는 final 상수만 생성자로 만든다
세션은 스프링이 시작될 때 무조건 있는 영역
[ f12 - 네트워크 - 리퀘스트 헤더 ] 에 쿠키 정보를 저장한다
유저 정보를 통째로 세션에 저장하는 방법은 일반적으로 사용되는 방식 중 하나. 유저의 고유한 식별값(예: 유저 이름, 아이디)을 키로 사용하여 해당 유저의 정보를 세션에 저장할 수 있다. = key에 내가 넣은 값(username 등)이 있을 때, 유저 정보를 통째로 넣어라. 그래야 편함
Share article