Java/Spring

_(underscore)๋กœ ์—ฐ๊ฒฐ๋œ uuid์™€ ์›๋ณธ ํŒŒ์ผ๋ช… ์ž˜๋ผ์„œ ๋ณด๊ด€ํ•˜๊ธฐ

๋ฒผ๋ฆฌ01 2024. 3. 16. 16:09

๐Ÿ“Œํ™˜๊ฒฝ

IntelliJ Ultimate

Java 17

 

Spring boot 3.2.3

Gradle - Groovy

Dependencies: 

Spring Web

Thymeleaf

Spring Data JPA

lombok

MariaDB 10.11

Spring Dev tool

 

๐Ÿ“Œ๋ฌธ์ œ

๊ฒŒ์‹œ๊ธ€ DTO์ธ `BoardDTO`๋ฅผ DB์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•ด ์—”ํ‹ฐํ‹ฐ์ธ `Board`์™€ `BoardImage`๋กœ ๋ณ€ํ™˜ํ•˜๊ณ ์ž ํ•œ๋‹ค.

`BoardDTO`๋Š” ํŒŒ์ผ ์ด๋ฆ„ `String`์ด ๋‹ด๊ธด `List`๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง„๋‹ค.

`BoardImage`๋Š” ๊ฐ๊ฐ uuid, ์›๋ณธ ํŒŒ์ผ๋ช…, ์ˆœ์„œ์ธ `uuid`,  `fileName`, `ord`๋ฅผ ํ•„๋“œ๋กœ ๊ฐ€์ง€๋ฉฐ ์ƒ์œ„ ์—”ํ‹ฐํ‹ฐ์ธ `Board`๋ฅผ ์ฐธ์กฐํ•œ๋‹ค. ์ด๋ฏธ์ง€๊ฐ€ ํฌํ•จ๋œ ๊ธ€ ์ž‘์„ฑ ์‹œ `BoardDTO`๋ฅผ ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋ฐ›์•˜์„ ๋•Œ DB์— ์ €์žฅํ•˜๊ธฐ ์œ„ํ•˜์—ฌ `BoardDTO`์™€ `List`์— ๋‹ด๊ธด `String`์„ ์—”ํ‹ฐํ‹ฐ์ธ `Board`์™€ ๊ทธ ํ•˜์œ„ ์—”ํ‹ฐํ‹ฐ์ธ `BoardImage`๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ณผ์ •์—์„œ, `BoardDTO`๊ฐ€ `uuid_์›๋ณธํŒŒ์ผ๋ช….ํ™•์žฅ์ž๋ช…`์˜ ํ˜•ํƒœ๋กœ `String`์„ ๊ฐ€์ ธ์™”์„ ๋•Œ `uuid`์™€ `fileName`์œผ๋กœ ๋‚˜๋ˆ„์–ด์ง„ ํ•„๋“œ๋กœ ๋”ฐ๋กœ ์ €์žฅํ•˜๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ•œ๋‹ค.

 

์ด๋•Œ  ํŒŒ์ผ๋ช…์„ `split("_")`์ฒ˜๋ฆฌํ•˜๋Š” ๊ฒฝ์šฐ ํŒŒ์ผ๋ช…์— `_`๊ฐ€ ํฌํ•จ๋˜์–ด ์žˆ์œผ๋ฉด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ํŒŒ์ผ๋ช…์ด ์ €์žฅ๋˜๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์—ฌ ํ•ด๊ฒฐํ•˜๊ณ ์ž ํ•œ๋‹ค.

 

 

//	์ด๋ฏธ์ง€ ํŒŒ์ผ๋ช… List๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๊ฒŒ์‹œ๊ธ€ DTO
//	BoardDTO

package org.zerock.b02.dto;

import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.Size;
import lombok.AllArgsConstructor;

import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;
import java.util.List;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class BoardDTO {

    private Long bno;

    @NotEmpty
    @Size(min = 3, max = 100)
    private String title;

    @NotEmpty
    private String content;

    @NotEmpty
    private String writer;
    private LocalDateTime regDate;
    private LocalDateTime modDate;

//    ์ฒจ๋ถ€ ํŒŒ์ผ ์ด๋ฆ„ ๋ชฉ๋ก
    private List<String> fileNames;
}

 

 

//	Board

package org.zerock.b02.domain;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.annotations.BatchSize;

import java.util.HashSet;
import java.util.Set;

@Entity
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@ToString(exclude = "imageSet")
public class Board extends BaseEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY) //  Auto Increment
    private Long bno;

    @Column(length = 500, nullable = false)
    private String title;

    @Column(length = 2000, nullable = false)
    private String content;

    @Column(length = 50, nullable = false)
    private String writer;


//    ์—”ํ‹ฐํ‹ฐ ๊ฐ์ฒด๋Š” ๋ถˆ๋ณ€ํ•˜๋„๋ก ์„ค๊ณ„ํ•˜๋Š” ๊ฒƒ์ด ์ข‹์œผ๋‚˜, ๊ฐ•์ œ์ ์ธ ์‚ฌํ•ญ์€ ์•„๋‹ˆ๋ฏ€๋กœ ์ˆ˜์ •์ด ํ•„์š”ํ•˜๋‹ค๋ฉด ์ˆ˜์ •์ด ๊ฐ€๋Šฅํ•œ ๋ถ€๋ถ„๋งŒ ๋ฉ”์„œ๋“œ๋กœ ์„ค๊ณ„ํ•œ๋‹ค
    public void update(String title, String content){
        this.title = title;
        this.content = content;
    }

    @OneToMany(mappedBy = "board", cascade = {CascadeType.ALL}, fetch = FetchType.LAZY, orphanRemoval = true)
    @Builder.Default
    @BatchSize(size = 20)   //  BatchSize ๊ฐ€ ์—†์œผ๋ฉด ํŽ˜์ด์ง€ ํ•˜๋‚˜๋ฅผ ๋ถˆ๋Ÿฌ์˜ฌ ๋•Œ ํŽ˜์ด์ง€ ๋‹น ๊ฒŒ์‹œ๊ธ€ ๊ฐœ์ˆ˜ ๋งŒํผ์˜ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•ด์„œ ๋ฌธ์ œ๊ฐ€ ๋จ. ์ด N๋ฒˆ์˜ ์ฟผ๋ฆฌ๋ฅผ ํ•œ๋ฒˆ์— ์‹คํ–‰ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ค€๋‹ค.
    private Set<BoardImage> imageSet = new HashSet<>();

//    Board ๊ฐ์ฒด ๋‚ด์—์„œ BoardImage๋ฅผ ๋ชจ๋‘ ๊ด€๋ฆฌํ•จ.
    public void addImage(String uuid, String fileName){

        BoardImage boardImage = BoardImage.builder()
                .uuid(uuid)
                .fileName(fileName)
                .board(this)
                .ord(imageSet.size())
                .build();

        imageSet.add(boardImage);
    }


//    BoardImage์˜ Board ์ฐธ์กฐ๋ฅผ null๋กœ ๋ณ€๊ฒฝํ•จ
    public void clearImage(){
        imageSet.forEach(boardImage -> {
            boardImage.changeBoard(null);
        });

        this.imageSet.clear();
    }


}

 

 

 

//	๊ฒŒ์‹œ๊ธ€์— ํฌํ•จ๋˜๋Š” ์ด๋ฏธ์ง€ uuid, ์›๋ณธ ํŒŒ์ผ๋ช…, ์ˆœ์„œ, ์ƒ์œ„ ์—”ํ‹ฐํ‹ฐ์ธ Board๋ฅผ ์ฐธ์กฐํ•จ
//	BoardImage

package org.zerock.b02.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.*;

@Entity
@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class BoardImage implements Comparable<BoardImage>{

    @Id
    private String uuid;

    private String fileName;
    private int ord;

    @ManyToOne
    private Board board;


    @Override
    public int compareTo(BoardImage other) {
        return this.ord - other.ord;
    }

    public void changeBoard(Board board){
        this.board = board;
    }

}

 

 

 

 

 

//	boardService ๋‚ด dtoToEntity ๋ฉ”์„œ๋“œ์˜ ์ผ๋ถ€

boardDTO.getFileNames().forEach(fileNames -> {
    String[] arr = fileNames.split("_");
    System.out.println(Arrays.toString(arr));
    board.addImage(arr[0], arr[1]);
});

 

//	dtoToEntity Test

@Test
public void dtoToEntityTest(){

    String[] strs = {"thisisuuid_aaa_a_.jpg", "thisisuuid_abc.jpg"};
    List<String> fileNames = new ArrayList<>(List.of(strs));

    BoardDTO boardDTO = BoardDTO.builder()
            .title("test title")
            .content("test content")
            .writer("test writer")
            .fileNames(fileNames)
            .build();

    Board board = boardService.dtoToEntity(boardDTO);
    log.info("dtoToEntity " + board);
    log.info("board imageSet " + board.getImageSet());
}

 

board imageSet [BoardImage(uuid=thisisuuid, fileName=abc.jpg, ord=1, board=Board(bno=null, title=test title, content=test content, writer=test writer)), 
			BoardImage(uuid=thisisuuid, fileName=aaa, ord=0, board=Board(bno=null, title=test title, content=test content, writer=test writer))]

 

 

`fileName`์— ์›๋ณธ ํŒŒ์ผ๋ช…์ธ `aaa_a_.jpg`์™€ `abc.jpg`๊ฐ€ ๋‹ด๊ฒจ์•ผํ•˜๋Š”๋ฐ `aaa`์™€ `abc.jpg`๊ฐ€ ์ €์žฅ๋˜์–ด์žˆ๋‹ค. `_`๊ฐ€ ํฌํ•จ๋œ ํŒŒ์ผ์ด๋ฆ„์˜ ๊ฒฝ์šฐ ํŒŒ์ผ๋ช…์ด ์˜จ์ „์น˜ ๋ชปํ•˜๊ฒŒ ์ž˜๋ ค ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ๊ฐ’์ด ์ €์žฅ๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

์˜ˆ๋ฅผ ๋“ค์–ด `uuid_aaa_a_.jpg`๋Š” `_`๋กœ splitํ•  ๊ฒฝ์šฐ `uuid` `aaa` `a` `.jpg`๋กœ ์ž˜๋ฆฌ๊ฒŒ ๋œ๋‹ค. `addImage()`๋ฉ”์„œ๋“œ์— `_`๋กœ ๋‚˜๋ˆˆ ๊ฐ’์˜ ์ฒซ๋ฒˆ์งธ String๊ณผ ๋‘๋ฒˆ์งธ String์„ ์ธ์ž๋กœ ๋„ฃ์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— `BoardImage`์˜ `uuid`์™€ `fileName`์€ `uuid`์™€ `aaa`๊ฐ€ ๋œ๋‹ค.

 

 

 

๐Ÿ“Œํ•ด๊ฒฐ

๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ์›์ธ์€ ์›๋ณธ ํŒŒ์ผ๋ช…์— `_`๊ฐ€ ์—†๋‹ค๋Š” ๊ฐ€์ • ํ•˜์— String์„ `_`๋กœ ๋‚˜๋ˆ„๋Š” ๋กœ์ง์„ ์ž‘์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์œ ์ €๊ฐ€ ์—…๋กœ๋“œํ•˜๋Š” ํŒŒ์ผ๋ช…์— `_`๊ฐ€ ํฌํ•จ๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋†’์œผ๋ฏ€๋กœ ์ด๋ฅผ ๋ณด์™„ํ•˜๊ธฐ ์œ„ํ•ด ์ฒซ๋ฒˆ์งธ `_`๊ฐ€ ์‹œ์ž‘๋˜๋Š” ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์•„ ๊ทธ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํŒŒ์ผ๋ช…์„ ์ž๋ฅด๊ธฐ๋กœ ํ–ˆ๋‹ค.

 

 

//	BoardService ๋‚ด dtoToEntity์˜ ์ผ๋ถ€


//	"_"๋กœ ๋‚˜๋ˆ„๊ฒŒ ๋˜๋ฉด "_"๊ฐ€ ์—ฌ๋Ÿฌ ๊ฐœ ํฌํ•จ๋œ ์ด๋ฏธ์ง€์ผ ๊ฒฝ์šฐ ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ ํŒŒ์ผ๋ช…์ด ์ €์žฅ๋˜๋Š” ์˜ค๋ฅ˜ ๋ฐœ์ƒ
//	String[] arr = fileNames.split("_");

//	๋งจ ์ฒ˜์Œ์— ์˜ค๋Š” "_"์˜ ์ธ๋ฑ์Šค๋ฅผ ์ฐพ์•„๋‚ด ๊ทธ ์ธ๋ฑ์Šค๋ฅผ ๊ธฐ์ค€์œผ๋กœ String ์„ ์ž˜๋ผ๋‚ธ๋‹ค.
int start = fileNames.indexOf("_");
String[] arr = {fileNames.substring(0, start), fileNames.substring(start + 1)};

System.out.println(Arrays.toString(arr));
board.addImage(arr[0], arr[1]);

 

 

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋Š” ์œ„์™€ ๋™์ผํ•˜๋ฏ€๋กœ ์ƒ๋žตํ•œ๋‹ค.

 

 

 

board imageSet [BoardImage(uuid=thisisuuid, fileName=abc.jpg, ord=1, board=Board(bno=null, title=test title, content=test content, writer=test writer)), 
			BoardImage(uuid=thisisuuid, fileName=aaa_a_.jpg, ord=0, board=Board(bno=null, title=test title, content=test content, writer=test writer))]

 

 

์›๋ณธ ํŒŒ์ผ ๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์ž…๋ ฅ๋œ ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.