상세 컨텐츠

본문 제목

10. JPA를 이용하여 @OneToMany 관계 설정하기

Java/코딩의 신

by Gopythor 2023. 12. 15. 20:03

본문

728x90
반응형

지난번에는 사용자와 권한을 many to many로 설정.

사용자와 게시글이 있는데, 여러개의 게시글을 작성할 수 있다.

사용자와 게시글 간의 관계가 1:N가 된다.

사용자는 게시글을 많이 작성할 수 있지만, 한 게시글은 한명의 작성자만 지닌다.

게시글 입장에서는 N:1

사용자테이블

보드 테이블

 

지금 텍스트로 홍길동이 작성되어 있다.

해당 내용을 username으로 작성될 수 있도록 하겠다.

게시글 조회하면 user테이블도 같이 조회.

username 같은 경우는 thymeleaf 문법으로 가져올 수 있지만,

user테이블의 다른 닉네임이나 다른 사용자 정보가 필요하다고 가정을 해서 같이 매핑을 해서 가져오겠다.

Board 클래스의 사용자 정보를 넣음.

게시글 입장에서는 Many to One임.

그 아래에 JoinColumn어노테이션.

어떤 컬럼과 user테이블이 연결이 될 지.

board 테이블에는 user테이블과 연결된 키 값이 없다.

user_id추가.

외래키값 설정

외래키 제약조건을 걸었기 때문에 자동완성되서 선택할 수 있게 나온다.

사용자 반영해서 저장.

key값을 사용해서 사용자 정보를 가져와서 테이블에 있는 정보를 사용할 것이다.

JoinColumn에 user_id를 적음.

referencedColumnName은 사용자 테이블의 어떤 컬럼과 연결이 됐는지.

id라고 적으면 된다 - PK값으로 설정되어있어서 생략을 해도 되긴 하다.

Board 클래스가 조회가 되서 페이지에 뿌려질 것이다.

홍길동 대신 board.user.username으로 가져오도록 할 것이다.

gildong으로 바뀐 것을 확인할 수 있다.

 

hi / hi 로 글작성.

글 작성은 됐는데 user id가 들어가있지 않다.

조회를 하려고 하면 사용자 id가 없으므로 에러가 발생함.

해당 행을 삭제.

글을 작성할 때에도 사용자 정보를 넣어줄 것임.

 

작성하는 부분은 BoardController의 post form

board.setUser(user);

유저의 key 값 참조.

로그인한 user이름을 페이지에서 직접 전달하게 되면, post 요청하는 부분을 자신의 id가 아닌 다른 사람 id 전달 할 수도 있음. 다른 사람 id 들어가는 결과가 나옴. 사용자가 보낸 인증정보는 믿어서는 안된다. 서버쪽에서 가진 인증 정보를 활용해서 직접 담아줘야 함.

인증정보를 Spring Security에서 해주고 있다. 인증 정보를 가져오는 방법은 여러가지가 있다. 그 중에 하나가 Authentication을 활용하는 것이다.

파라미터를 선언해주면 인증정보가 알아서 날라옴.

사용자 이름을 담을 수 있음.

username을 이용해서 값을 처리.

인증 정보를 가져오는 방법 말고도 스프링에서는 다양한 방식이 있다.

컨트롤러에서는 Authentication을 이용할 수 있지만,

서비스 같은 곳에서는 전역변수인 SecurityContextHolder 방식으로도 가져올 수 있다.

 

컨트롤러에서 Pricipal을 받아서 가져올 수 있다.

동일하게 getName.

Authentication을 이용해도 된다.

정보를 가져와서 boardRepository를 이용해서 저장을 했는데, 해당 부분을 BoardService 클래스를 만들어볼 것이다.

BoardService

인자로 username과 board 전달.

username을 바탕으로 id를 불러올 것임.

UserRepository를 불러옴.

UserReposity에 아무것도 없음.

위와 같이 하나 선언해줄 것임.

컬럼에 일치하는 사용자데이터를 가져옴.

하나의 데이터나 없으면 Null 값이 들어올 것이다.

지금같은 경우는 unique 제약 조건 설정이 있어서 여러 조건이 오지는 않는다.

user클래스를 받았고, board의 setUser.

그리고 저장을 함.

 

해당 내용을 재실행해볼 것이다.

user id가 잘 들어감.

사용자 이름과 게시글을 함께 저장해봄.

사용자 조회를 할 때에도 게시글을 나오게 할 수 있음.

Api로 하면 좋을 것 같음.

BoardApiController를 복사해서 붙임.

board를 user로 변경.

기존에 all은 검색조건 다 지움.

새로운 값 설정하는 것인데 주석처리 할 것이다.

User 클래스에 Board를 마찬가지로 설정해줄 것이다.

사용자 입장에서는 게시글을 가지고 올 때, One To Many가 될 것이다.

그리고 List로 가져 올 것이다.

매핑 정보를 어떤 컬럼을 참조할 것인지, Join 컬럼에 작성을 해 뒀다.

해당 정보를 동일하게 작성하지는 않음.

변수명을 작성한다.

user변수에 적어뒀던 그것을 그대로 사용.

아까는 Board 클래스만 user를 가지고 있었다.

지금은 서로가 서로의 클래스를 가지고 있다.

기존에는 단방향 매핑, 지금은 양방향 매핑.

서로 조회할 수 있게 처리함.

One to Many는 가장 많이 사용되는 조인.

One to Many 매핑 시킬 때에는 Many쪽에서 소유하는 쪽을 많이 적음.

many쪽에 FK를 가지고 있음.

One쪽에 해당하는 곳은 mappedBy를 적어준다.

 

 

Postman으로 보냈으나 로그인하라는 에러가 발생.

로그인 없이 요청 가능하도록 하겠음.

그리고 또 한가지 봐야 할 것이 있다.

사용자가 role을 가지고 있다.

role이 서로를 참조함.

role 안에 사용자가 있고, 사용자 안에 role이 있어서 재귀적으로 반복이됨.

Json 연결을 끊을 것임.

role 안에 작성.

role을 가지고 있는 사용자들은 표시가 되지 않을 것임.

 

 

권한 쪽은 해결이 됐는데, Board쪽에서 재귀 문제 발생.

board 쪽에도 연결을 끊음.

정상적으로 표시가 됨.

가장 상위에 사용자 정보가 있음.

board 안에 사용자가 표시 안되도록 함.

 

새로 회원 가입을 해볼 것임.

gildong2

hi2로 글 작성.

gildong2로 표시.

글을 수정해볼 것임.

gildong2가 hi2 작성함.

12가 gildong2의 id가 됨.

포스트를 하기 위해서는 user의 id로 전송하면 됨.

user의 12이다.

Body부분을 JSON으로 변경.

boards 리스트를 담아서 보내줄 것이다.

users의 PutMapping에 걸린다.

User레파지토리를 저장해서 User클래스에 정의된 대로 수정이 될 것임.

Board 클래스의 정보를 바꾸려고 하는 것임.

원래는 Board레파지토리로 하는 것이 일반적임.

그러나 User를 수정해서 Board 안의 내용을 바꿀 수 있음.

이렇게 작성을 해보겠음.

브레이크 포인트를 걸어보겠음.

send를 보내니 다시 로그인하라는 메시지가 뜸.

개발자도구를 보면 csrf가 있음.

Spring Security에서 자동으로 생성이 됨.

form과 input 사이에 하나의 input이 들어간 것임.

th:action을 주게되면 csrf 값이 활성화 인증이 되어 있으면 들어가게 됨.

Forgery의 뜻은 위조.

해커가 자바스크립트를 심는다던지 함.

위조 이메일을 보내서 클릭하면 로그인이 되어 의도하지 않은 동일한 요청을 하게 할 수 있음.

Key값과 포스트 요청 같이 전달을 해줘서, 일치하는지 확인해서 서버와 일치하면 요청이 정상적으로 됨.

일치하지 않으면 수행하지 않음.

그 과정은 Spring Security가 해주고 있음.

 

csrf를 무력화.

실제로 사이트 운영할 때에는 보완에 취약해질 것임.

테스트 원활하게 하기 위한 설정.

postman에서도 csrf 값도 같이 전송할 수 있음.

그러나 설정이 번거로움.

조회된 값이 나옴.

hi3으로 보냈는데, board에 아무것도 반영이 되어 있지 않음.

postman에서 보낸 hi3가 정상적으로 도착함.

user.setBoard(newUser.getBoards()) 추가.

요청.

user의 hi3가 board에 들어가 있다.

그러나 hi3가 들어가있지 않다.

사용자 레파지토리이기때문에 board가 저장이 되고 있지 않음.

패스워드 같은 경우는 저장이 되지만, One to Many같은 경우는 저장이 안됨.

외부클래스의 엔티티는 저장이 안됨.

매핑된 클래스도 전부다 DB에 반영을 하고 싶으면, cascade 설정을 해줘야 한다.

ALL로 지정하면 5가지 기능을 전부 사용하겠다는 뜻임.

JPA를 사용하고 있는데, 스펙이다.

실질적으로 스프링부트에서 사용하면 Hibernate를 구현체로 하는 것을 기본적으로 사용한다.

그러나 Hibernate를 직접 호출하지는 않고, Spring Data JPA를 이용해서,

레파지토리를 이용해서  인터페이스만 정의해놓으면 구현체들이 알아서 처리를 함.

Hibernate의 많은 코드들을 사용하지 않고 간편하게 save나 지정하는 것만으로도 사용할 수 있음.

 

메소드들은 저장할 때, 레파지토리의 세이브 등, ctrl+alt+b를 누르면 구현체를 쫓아간다

새로운 데이터일 경우에는 persist를 하고, 기존에 있는 데이터는 merge를 함.

직접 이런 코드를 작성하지는 않지만, 내부적으로는 작성이 되어 있음.

옵션이 해당 뜻인 것임.

remove 옵션을 주게 되면,

사용자와 게시판이 연결이 되어 있고, 외래키가 걸려 있어서 user가 삭제가 안된다.

Cascade를 쓰면 해당 user의 Board 정보가 삭제가 되고, 사용자도 삭제가 된다.

게시글과 사용자가 모두 삭제.

때로는 유용하고 안 할 수도 있다.

 

All로 지정해주겠다.

hi3라는 글이 사용자를 저장하면 같이 게시글이 저장이 되는지 확인.

저장은 됐지만, user_id가 저장되지 않았다.

사용자 정보를 담는 코드가 없다.

루프를 돌아서, 지금 조회한 user를 담아주면 됨.

hi3에 정상적으로 user_id가 반영됨.

또 유용하게 사용할 수 있는 옵션이 orphanRemoval이 있다.

기본값은 false이다.

application.properties의 쿼리를 보도록 하자.

지금은 DB처리 메소드 호출을 했을 때, 어떤 SQL이 실행이 되는지 찍히지 않고 있다.

show-sql도 있지만 추천하지 않는다.

왜냐하면 log framework와 최적화가 되어 있지 않기 때문이다.

또한 prepared statements가 기록되지 않는다.

해당 방법으로 진행을 해보겠다.

logging.level.org.hibernate.SQL=DEBUG
logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE

해당 옵션을 주면 SQL이 기록이 될 것임.

게시글 조회를 하게 되면,

어떤 쿼리를 실행 했는지 알 수 있음.

orphanRemoval을 false로 테스트해보겠다.

기본에 있던 Boards를 새로운 데이터로 바꾸고, Setting을 해서 저장을 함.

 

한번 id 값을 줘보도록 함.

물론 새로운 값이 들어갈 수 있지만,

board의 기존 데이터, 67번 데이터를

 

hi4로 변경해볼 것임.

또 하나 주목할 점이 hi2가 있다는 점이다.

hi4로 바뀜.

user안에는 JSON으로 보낸 Board밖에 없다.

하지만 기존 데이터는 아무런 영향이 없다.

 

orphanRemoval을 true로 줘볼 것이다.

67번을 hi5로 변경

에러가 발생함.

Board 엔티티로부터 해당 콜렉션이 참조되고 있지 않다.

이유를 구글링해보면,

새로운 Board를 User의 Board로 셋팅을 했는데,

기존의 List를 Clear하고 addAll을 이용을 해서 새로운 리스트를 셋팅.

인스턴스를 바꾸지 않는 것이다

기존의 Data는 전부다 삭제.

지금 받은 데이터로 전부 바꿔줌.

gildong2가 가지고 있는 게시글이 hi2, hi4이다.

 

 

hi5를 보냄.

hi2는 사라지고, hi5만 남음.

clear를 했기 때문에 최종적으로 인스턴스에 있는 데이터, user에 있는 Board만 남음.

나머지 데이터는 지워짐.

orphan Removal.

고아라는 뜻.

부모가 없는 데이터는 지운다.

일일이 지우는 코드를 작성하지 않아도 간단히 몇줄의 코드만으로 부모가 없는 데이터 삭제.

gildong2를 삭제해볼 것임.

삭제를 하면 게시글도 같이 삭제가 될 것임.

user 삭제를 보냄.

Gildong2가 작성한 데이터가 삭제가 됨.

사용자도 삭제가 됨.

https://youtu.be/wM7P-6_CHFM?si=K8blh3MKdw4C9nMQ

728x90
반응형

관련글 더보기

댓글 영역