우당탕탕 개발일지

16일차_게시판 프로젝트(9) 본문

개발 프로젝트/게시물 프로젝트

16일차_게시판 프로젝트(9)

ujin302 2024. 3. 26. 00:13
반응형

1. File Entity 설계 및 연관관계 

1-1. 설명 

게시물 하나에 파일 N개를 등록할려고 한다. 

이때 파일을 관리하는 Table(tb_board_file)를 하나 생성하여 관리할 것이다. 

 

게시물 테이블인 tb_board과 tb_board_file 에게 연관관계 부여한다.

연관관계를 통해 하나의 게시물에 따른 여러개의 첨부파일을 관리한다. 

 

연관관계 유형 >> 1 : N (게시물 : 파일) 

 

tb_board 에서 한행을 조회하면 tb_board_file 에서 해당 파일의 모든 행을 가지고 온다. 

 

 

 

1-2. 연관관계란? 

[ 정의 ]

연관 관계란 객체의 참조와 테이블의 외래 키를 매핑시키는 것

 

[ 연관관계 매핑 ]

우리는 JPA를 사용하므로써 entity들의 연관 관계를 매핑해두고 필요할 때

해당 entitiy와 연관된 entitiy를 사용하며 좀 더 객체 지향적인 프로그래밍을 할 수 있다.

 

→ JPA는 ORM을 이용하고 , ORM은 관계형 데이터 베이스를 연결시켜주는 것이니까 객체 (entity)마다 연결을 할 수 있는 것.

 

[참고]

https://velog.io/@nyong_i/JPA-%EC%97%B0%EA%B4%80-%EA%B4%80%EA%B3%84

 

[JPA] 연관 관계

연관관계에 대해서 알아보자.

velog.io

https://velog.io/@dmsgp8292/%EA%B4%80%EA%B3%84%ED%98%95-DB-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EB%A7%A4%ED%95%91%ED%95%98%EA%B8%B0

 

JPA 관계형 DB 연관관계 매핑하기

연관관계 매핑을 하는 방법에 대해서 간단하게 정리를 해보았다.우리는 JPA를 사용하므로써 entity들의 연관 관계를 매핑해두고 필요할 때 해당 entitiy와 연관된 entitiy를 사용하며 좀 더 객체 지향

velog.io

 

 

 

1-3. 기본키? 외래키? 

[ 기본키 ]

- 정의 : 테이블에서 각 행의 정보를 식별할 수 있는 키 

 

- 특징 

  • 유일성 O 
  • 중복 X, Null X 
  • 해당 PK필드는 다른테이블의 필드에서  참조 당함 

- 예시 

주민등록번호

 

[ 외래키 ]

- 정의 : 다른 테이블의 PK를 통해 참조 

 

- 특징 

  • 1 : N 관계에서 N에 해당 하는 쪽에 외래키 지정 

외래키 : board_id

 

 

[참고]

https://velog.io/@jch9537/DATABASE-PK-FK

 

[DATABASE] 기본키(PK), 외래키(FK)

데이터 모델링을 하며 ERD(Entity Relationship Diagram: 개체관계도)에 테이블을 만들면서 테이블간의 관계 연결에 대해 어려움을 겪었다.요약하면 부모와 자식/ 참조하는 테이블과 참조되는 테이블로

velog.io

https://keembloo.tistory.com/42

 

데이터베이스 기본키(PK), 외래키(FK)

1 . 기본키 식별키 Primary Key (PK) 테이블의 유일한 값을 가지는 필드 데이터의 중복이 없는 식별가능한 필드를 선정 테이블 1개당 PK필드 1개 이상 (권장) 중복 , null , 공백은 사용불가 해당 PK필드는

keembloo.tistory.com

 

 

 

1-4. 소스 

 

BoardFileEntity.java

자식 Entity

파일 정보를 관리하는 Entity -> Table 이름 : tb_board_table

 

1. 컬럼명 설정 

  • id : PK
  • originalFileName : 실제 파일명
  • storedFileName : 서버 저장용 파일명 

 

2. 외래키 지정 

  • 컬럼명 : board_id
  • 연관관계 : @ManyToOne (N : 1 >> 파일 N개 : 게시물 1개) >> 자식 Entity
  • FetchType.LAZY : 지연로딩 >> 연관관계에 있는 Entity를 가져오지 않고 필요한 경우에 호출하여 데이터 요청   

3. private BoardEntity boardEntity; (변수 선언)

  • tb_board 테이블의 기본키인 Long 타입이 아닌 BoardEntity 타입 사용 
  • 실제 DB에서는 Long 타입으로 저장 

 

 

BoardEntity.java

부모 Entity

게시물 정보 관리 Entity > Table 이름 : tb_board

 

1. 연관관계 : @OneToMany (1 : N >> 게시물 1개 : 파일 N개) >> 부모 Entity

2. mappedBy : 어떤 것과 매핑할 것인지 >> 연관관계에서 주인

BoardFileEntity에서 선언한 BoardEntity 타입의 변수명과 동일한 이름 사용 

3. CascadeType.REMOVE >> 1-6 참고

4. orphanRemoval = true >> 1-6 참고 

5. fetch 위와 동일 

 

 

 

 

[참고] 양방향 연관관계

https://velog.io/@goniieee/JPA-OneToMany-ManyToOne%EC%9C%BC%EB%A1%9C-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84-%EA%B4%80%EB%A6%AC%ED%95%98%EA%B8%B0

 

JPA @OneToMany, @ManyToOne으로 연관관계 관리하기

안녕하세요, 오늘은 스프링을 이용하면서 자주 쓰는 JPA에 대해서 이야기해보려고 합니다. JPA는 스프링 개발을 하면서 이제 거의 필수가 된 ORM 기술입니다. @OneToMany, @ManyToOne 어노테이션은 1:N, N:1

velog.io

 

[참고] Fetch

https://velog.io/@may_yun/JPA-Fetch-%EC%A0%84%EB%9E%B5

 

[JPA] Fetch 전략

애플리케이션이 DB로부터 데이터를 가지고 오는 것DB와 통신하여 데이터를 읽는 것에는 큰 비용이 소모되기 때문에 이를 해결하는 전략fetch 전략에는 FetchType.EAGER(즉시로딩), FetchType.LAZY(지연로딩

velog.io

 

 

 

1-5. 결과 화면 

 

 

 

1-6. 부모 Entity 관련 설정

[ Cascade ]

>> 엔티티의 상태 변화를 전파시키는 옵션

 

[ orphanRemoval]

>> 고아 객체를 제거하는 옵션

 

  부모 Entity 부모, 자식 Entity 연관관계 
삭제 삭제 변경
CascadeType.REMOVE 자식 Entity 삭제 O

자식 엔티티가 DB 삭제 X
외래키 값만 변경
자식 엔티티가 DB에 삭제 X
외래키 값만 변경
orphanRemoval = true 자식 엔티티가 DB에서 삭제 O
(고아 객체로 취급)

 

[참고] 정의 

https://yarisong.tistory.com/35

 

Spring Data JPA -Cascade와 orphanRemoval?

1. CasCade란? 엔티티의 상태 변화를 전파시키는 옵션이다. 간단히 이야기하면 특정 엔티티를 변화시킬때 그에 따라 연관된 엔티티도 같이 상태가 변한다는 의미이다. 2. CasCade Type? 총 6가지의 Type

yarisong.tistory.com

 

[참고] 비교

https://velog.io/@yuseogi0218/JPA-CascadeType.REMOVE-vs-orphanRemoval-true

 

JPA - CascadeType.REMOVE vs orphanRemoval = true

CascadeType.REMOVE 와 orphanRemoval = true 옵션이 각각 고아객체를 어떻게 처리하는지 알아보았습니다.

velog.io

 

[참고] 부모? 자식?

https://velog.io/@songunnie/JPA-%EC%97%B0%EA%B4%80%EA%B4%80%EA%B3%84%EC%97%90-%EC%9E%90%EC%A3%BC-%EC%93%B0%EC%9D%B4%EB%8A%94-property%EB%93%A4

 

[JPA] 연관관계에 자주 쓰이는 property들

부모 엔티티: 먼저 자료가 저장되어 있어야 할 쪽자식 엔티티: 부모 엔티티의 PK를 FK로 참조하는 쪽주식별관계: 부모엔티티의 PK를 자식엔티티의 PK로 참조비식별관계: 부모엔티티의 PK를 자식엔

velog.io

 

 

 

1-7. 유튜브 영상

https://www.youtube.com/watch?v=UU2xVPXBh34&list=PLV9zd3otBRt7jmXvwCkmvJ8dH5tR_20c0&index=17

 


 

2. 파일 DB 저장

첨부파일 있을 경우에 따른 데이터 저장! 

 

지난 게시물(15일차, 3-2 참고)에 이어서 6, 7번 작업 

15일차 (3-2 참고)  : 2024.03.22 - [취준] - 15일차_게시판 프로젝트(8)

2-1. 작업 순서

6. tb_Board 에 해당 데이터 save 처리 (-> 게시물 관련 Data)
7. tb_Board_File 에 해당 데이터 save 처리 (-> 첨부파일 관련 Data)

 

BoardService.java

>> 받아온 Data를 DB에 저장 

public void save(BoardDTO boardDTO) throws IOException {
    // throws IOException : save 메소드에서 발생한 예외를 상위 메소드인 BoardService 에서 처리하기 위해 사용
    // 상위에서 처리하는게 더 올바른 경우도 있고 해당 메소드에서 처리하는게 더 올바른 경우가 있다!

    // 파일 첨부 여부에 따라 로직 분리
    if(boardDTO.getBoardFile().isEmpty()) {
        // 첨부 파일 X

        // 1. DTO의 값들을 Entity에 옮겨담는 함수 호출
        BoardEntity boardEntity = BoardEntity.toSaveEntitiy(boardDTO);

        // 2. DB에 저장 (tb_board)
        // save : JPA가 가지고 있는 함수
        boardRepository.save(boardEntity);
    } else {
        // 파일 첨부 ㅇ
        /*
            1. DTO에 담긴 파일 추출 (실제 파일)
            2. 파일의 이름  추출
            3. 서버 저장용 이름 생성
                : 내사진.jpg -> 8397980824_내사진.jpg
            4. 저장 경로 설정
            5. 해당 경로에 파일 저장
            6. tb_Board 에 해당 데이터 save 처리 (-> 게시물 관련 Data)
            7. tb_Board_File 에 해당 데이터 save 처리 (-> 첨부파일 관련 Data)
         */
        MultipartFile boardFile = boardDTO.getBoardFile(); // 1
        String originalFilename = boardFile.getOriginalFilename(); // 2
        String storedFileName = System.currentTimeMillis() + " " + originalFilename; // 3
        String savePath = "E:/Board_File/" + storedFileName; // 4. 실제 존재하는 경로 값
        boardFile.transferTo(new File(savePath)); // 5
        // transferTo : 파일 저장 메소드 & 예외 발생 가능성으로 인해서 throws IOException 사용

        // 6
        BoardEntity boardEntity = BoardEntity.toSaveFileEntitiy(boardDTO); // 6-1. DTO -> Entity (id값이 없다)
        Long savedId = boardRepository.save(boardEntity).getId(); // 6-2. Entity 객체 저장(tb_board) & 저장한 id 가져오기
        System.out.println("저장한 ID (첨부파일 O ) : " + savedId);
        BoardEntity board = boardRepository.findById(savedId).get(); // 6-3. 저장한 id에 대한 Data 가져오기

        // 7
        BoardFileEntity boardFileEntity = BoardFileEntity.toBoardFileEntity(board, originalFilename, storedFileName); // 7-1. File Entity에 저장
        boardFileRepository.save(boardFileEntity); // 7-2. DB에 저장 (tb_board_file)
    }
}

 

 

 

2-2. tb_Board 에 해당 데이터 save 처리

>> 게시물 관련 Data

 

BoardService.java

1. 객체 변환 

  • toSaveFileEntity : DTO -> Entity 
  • 아래 BoardEntity.java 소스 참고

2. savedId

  • boardEntity 객체를 DB(tb_baord)에 저장 
  • 저장한 Row의 Id값 가져오기 

3. board

  • 찾은 Id 값으로 해당 Row 데이터 가져오기

 

 

BoardEntity.java

>> toSaveFileEntity 

 

1. 역할 : DTO -> Entity 

2. 게시물 기본 Data 변환

3. 첨부파일이 있는 경우임으로  setFileAttached 값 '1' 로 설정 

 

 

 

2-3. tb_Board_File 에 해당 데이터 save 처리

>> 파일 관련 Data

 

BoardService.java

1. boardFileEntity

  • 해당 함수를 통해 File Entity 초기화
  • board >> 바로 위에서 특정 id값에 대한 Row의 데이터가 저장되어 있음 
  • BoardFileEntity class에 정의되어 있는 함수

2.   save 

  • DB (tb_board_file)에 저장 
  • BoardFileRepository 인터페이스의 save 기능 사용 

 

 

BoardFileEntity.java

>> toBoardFileEntity

 

1. 매개변수 

  • boardEntity : 게시물 기본 Data 
  • originalFileName : 실제 파일명 
  • storeFileName : 서버 저장용 파일명 

2. 역할

  • File Entity 초기화

 

 

BoardFileRepository.java

1. Jpa 상속 받음 

2. 파일 관련된 데이터 작업을 위한 Repository

3. BoardFileRepository 어노테이션 생략 가능 

 

[참고] BoardFileRepository 어노테이션 생략 

https://kingds.tistory.com/46

 

Repository Interface에 @Respoitory가 없는 이유

Notion Link www.notion.so/Repository-Interface-Respoitory-6d78269d33d54e828ffb219153965eb7 Repository Interface에 @Respoitory가 없는 이유 @Repository 는 컴포넌트 스캔 뿐만이 아니라 JPA의 예외를 스프링에서 공통적으로 처리

kingds.tistory.com

 

 

 

2-4. 유튜브 영상  

https://www.youtube.com/watch?v=woNhl5skn-g&list=PLV9zd3otBRt7jmXvwCkmvJ8dH5tR_20c0&index=18

 

 

 

 

3. 게시글 조회 시, 파일첨부 이미지 출력

3-1. 첨부파일 관련 정보 DTO로 객체 변환 

 

BoardDto.java

1. 추가

  • getFileAttached : 0 or 1 

2. 파일이 있을 경우 

  • 원본 파일명과 서버 저장용 파일명 저장해야 함 -> 해당 데이터는 file Entity에 존재함 
  • 부모 Entity인 BoardEntity를 통해서 자식 file Entity의 값 중 1번쨰 요소를 들고옴. 

 

 

detail.html

1. 첨부파일이 있을 경우에만 해당 부분 노출 

2. 파일명 노출 

 

 

3-2. 정적 리소스 

WebConfig.java

addResourceHandler  : "/upload/" 로 시작하는 모든 요청에 대해 핸들러 동작  

addResourceLocations: 실제 리소스가 존재하는 외부 경로를 지정

 

[ 참고 ] WebMvcConfigurer

https://jake-seo-dev.tistory.com/605

 

스프링 WebMvcConfigurer 인터페이스란?

WebMvcConfigurer 란? 스프링 프레임워크에서 제공하는 인터페이스이다. 보일러플레이트 코드 없이 요구사항에 맞게 프레임워크를 조정할 수 있게 해준다. 특정한 스프링 클래스를 구현하거나 상속

jake-seo-dev.tistory.com

[ 참고 ] Configuration

https://jaeano.tistory.com/entry/Spring-Boot-Configuration-Component-Configuration-Bean

 

[Spring Boot] Configuration Component (@Configuration, @Bean)

이번에는 @Configuratoin 어노테이션에 대해 알아 볼 것이다. 우선 Configuration이란? 구성이라는 뜻이다. 스프링에서도 같은 의미도 사용된다. 사용도 특별하지 않다. 다른 어노테이션들 처럼 클래스

jaeano.tistory.com

[ 참고 ] addResourceHandler  

https://wildeveloperetrain.tistory.com/41#google_vignette

 

Spring Boot 프로젝트 외부 경로 파일 접근하기 addResourceHandlers

Spring Boot 프로젝트 외부 경로에 있는 파일 접근하기 addResourceHandlers 실서버에서 배포하여 실행중인 웹 애플리케이션 상에서 웹 사이트에 접속하여 파일 및 이미지 업로드를 실시할 때, 이미지 업

wildeveloperetrain.tistory.com

 

 

 

3-3. 결과화면

 

 

3-4. 유튜브 영상

https://www.youtube.com/watch?v=L_osf9DqqY4&list=PLV9zd3otBRt7jmXvwCkmvJ8dH5tR_20c0&index=19

 

반응형