πŸ’‘ κ²Œμ‹œνŒ - 파일 μ—…λ‘œλ“œ

박상민·2023λ…„ 9μ›” 9일
0
post-thumbnail

μ΄λ²ˆμ—λŠ” κ²Œμ‹œνŒμ˜ 파일 μ—…λ‘œλ“œμ— κ΄€ν•΄μ„œ μž‘μ„±μ„ ν•˜κ² λ‹€. 이번 글은 μ—…λ‘œλ“œλ§Œ 닀루고 파일 λ‹€μš΄λ‘œλ“œμ— κ΄€ν•΄μ„œλŠ” 더 곡뢀λ₯Ό ν•˜κ³  μž‘μ„±μ„ ν•˜κ² λ‹€.


μš°μ„  λ‹€μŒμ˜ 2가지가 ν•„μš”ν•˜λ‹€

  • 파일이 μ €μž₯된 κ²½λ‘œμ™€
  • 파일의 이름

mariaDB에 μ»¬λŸΌμ„ μΆ”κ°€ν•˜κ² λ‹€.

filename : 파일의 이름에 ν•΄λ‹Ήν•˜λŠ” 컬럼
filePath : 파일의 μ €μž₯된 κ²½λ‘œμ— ν•΄λ‹Ήν•˜λŠ” 컬럼

DB의 Table을 μˆ˜μ •ν–ˆμœΌλ‹ˆ Entity λ˜ν•œ μˆ˜μ •μ„ ν•˜κ² λ‹€.

Board

@Entity
@Data
public class Board extends TimeEntity{

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;

    private String title;
    private String content;
    private String author;

	// 파일 μ—…λ‘œλ“œλ₯Ό μœ„ν•œ ν•„λ“œ μΆ”κ°€
    private String filename;
    private String filepath;

    //μˆ˜μ •μ„ μœ„ν•œ μƒμ„±μž
    public Board(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    //κΈ°λ³Έ μƒμ„±μž μΆ”κ°€!
    public Board() {

    }
}

DB의 ν…Œμ΄λΈ”μ— filename, filepathλ₯Ό μΆ”κ°€ν–ˆκΈ°μ— Entity인 Board에도 private Strinf filename, private String filepathλ₯Ό μΆ”κ°€ν•œλ‹€.

EntityκΉŒμ§€ μˆ˜μ •μ„ ν–ˆμœΌλ‹ˆ κ²Œμ‹œκΈ€ μž‘μ„± νŽ˜μ΄μ§€λ₯Ό μˆ˜μ •ν•΄μ„œ 파일 μ—…λ‘œλ“œ 뢀뢄을 λ§Œλ“€μ–΄μ£Όκ² λ‹€.

boardWrite.html

<body>
<div class="container">
    <h1>κ²Œμ‹œκΈ€ μž‘μ„±</h1>
    <form th:action="@{/board/write}" method="post" enctype="multipart/form-data"> <!--μΆ”κ°€λœ λΆ€λΆ„-->
        <div class="form-group">
            <label for="title">제λͺ©</label>
            <input type="text" id="title" name="title" required>
        </div>
        <div class="form-group">
            <label for="author">μž‘μ„±μž</label>
            <input type="text" id="author" name="author" required>
        </div>
        <div class="form-group">
            <label for="content">λ‚΄μš©</label>
            <textarea id="content" name="content" rows="8" required></textarea>
        </div>
        <!--μƒˆλ‘œ μΆ”κ°€λœ λΆ€λΆ„-->
        <input type="file" name="file">
        <!---->
        <button type="submit" class="btn">μž‘μ„± ν•˜κΈ°</button>
    </form>
</div>
</body>

파일 μ—…λ‘œλ“œλ₯Ό ν•΄μ£ΌκΈ° μœ„ν•΄μ„œλŠ” 속성을 μΆ”κ°€ν•΄μ€˜μ•Όν•œλ‹€.
boardWrite의 form에 enctype="multipart/form-data이 μΆ”κ°€λ˜μ—ˆλ‹€.
λ˜ν•œ, μž…λ ₯창인 <input type="file" name="file">μΆ”κ°€λ˜μ—ˆλ‹€. 이제 싀행을 ν•΄μ„œ 확인을 ν•΄λ³΄μž.


κ²Œμ‹œκΈ€ μž‘μ„± νŽ˜μ΄μ§€

κ²Œμ‹œκΈ€ μž‘μ„± νŽ˜μ΄μ§€λ‘œ 였면 μœ„ 사진 처럼 νŒŒμΌμ„ 선택할 수 μžˆλŠ” νƒœκ·Έκ°€ μΆ”κ°€λœ 것을 확인할 수 μžˆλ‹€.
κ·ΈλŸ¬λ‚˜ 아직 νŒŒμΌμ„ μ„ νƒν•΄μ„œ μž‘μ„±μ„ 해도 DB에 μ €μž₯λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ— μ½”λ“œλ₯Ό μΆ”κ°€ν•˜κ³  μ—…λ‘œλ“œν•œ νŒŒμΌμ„ μ €μž₯ν•  디렉토리λ₯Ό ν•˜λ‚˜ λ§Œλ“€κ² λ‹€.

resources/static/files

μš°μ„  νŒŒμΌμ„ μ €μž₯ν•  디렉토리λ₯Ό ν•˜λ‚˜ λ§Œλ“€μ–΄μ€€λ‹€.


BoardService/save μˆ˜μ • μ „

public Board save(Board board) {
     return boardRepository.save(board);
}

BoardService/save μˆ˜μ • ν›„

public void save(Board board, MultipartFile file) throws Exception {

    //μ €μž₯ν•  경둜λ₯Ό 지정
    String projectPath = System.getProperty("user.dir") + "/src/main/resource/static/files";

    // 랜덀 μ‹λ³„μž 생성
    UUID uuid = UUID.randomUUID();

    // uuid + _ + 파일의 μ›λž˜ 이름
    String fileName = uuid + "_" + file.getOriginalFilename(); 

    //νŒŒμΌμ„ 생성할 것인데 κ²½λ‘œλŠ” projectPath, 이름은 filename둜 λ‹΄κΈ΄λ‹€λŠ” 뜻
    File saveFile = new File(projectPath, fileName);

    file.transferTo(saveFile);
    
    board.setFilename(fileName); //DB에 filename μ €μž₯
    board.setFilepath("/files/" + fileName); //DB에 파일 μ €μž₯ 경둜 μ €μž₯
        
    boardRepository.save(board)    
}

κ²Œμ‹œκΈ€μ„ μž‘μ„±ν•˜λŠ” μ½”λ“œμΈ save λ©”μ†Œλ“œμ— MultipartFile file λ§€κ°œλ³€μˆ˜λ₯Ό μΆ”κ°€ν•œλ‹€. μ΄λŠ” νŒŒμΌμ„ λ°›λŠ” 역할을 ν•œλ‹€.

μ΄λ•Œ μ£Όμ˜ν•  것이 μžˆλ‹€. μ €μž₯ν•  경둜λ₯Ό 지정할 λ•Œ μœˆλ„μš°μ™€ λ§₯의 경우 λ‹€λ₯Έ 뢀뢄이 μ‘΄μž¬ν•œλ‹€.

  • μœˆλ„μš°
    • "\\src\\main\\resource\\static\\files";
  • λ§₯
    • "/src/main/resource/static/files";

κ²Œμ‹œκΈ€ μž‘μ„± λ©”μ„œλ“œμΈ saveλŠ” μ»¨νŠΈλ‘€λŸ¬μ™€ κ²Œμ‹œκΈ€ μˆ˜μ •μ—μ„œλ„ μ‚¬μš©μ΄ λœλ‹€. save에 λ§€κ°œλ³€μˆ˜λ₯Ό μΆ”κ°€ν–ˆμœΌλ‹ˆ μ»¨νŠΈλ‘€λŸ¬μ™€ μˆ˜μ • 뢀뢄도 μˆ˜μ •μ„ ν•΄μ£Όκ² λ‹€.

BoardControll

@PostMapping("/board/write")
public String boardWrite(Board board, Model model, MultipartFile file) 
throws Exception {

    boardService.save(board, file);
    model.addAttribute("message", "κΈ€ μž‘μ„±μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
    model.addAttribute("searchUrl", "/board/list");

    return "message'
}

맀개 λ³€μˆ˜ MultipartFile file을 μΆ”κ°€ν–ˆλ‹€.


BoardService/update

public void update(Integer id, Board updateBoard, MultipartFile file) 
throws Exception {

    Board board = boardRepository.findById(id).get();
    board.setTitle(updateBoard.getTitle());
    board.setAuthor(updateBoard.getAuthor());
    board.setContent(updateBoard.getContent());

    save(board, file);
}

BoardController/postUpdate

@PostMapping("/board/post/update/{id}")
public String postUpdate(@PathVariable Integer id, Board board, 
Model model, MultipartFile file) throws Exception {

    boardService.update(id, board, file);

    model.addAttribute("message", "κ²Œμ‹œκΈ€ μˆ˜μ •μ΄ μ™„λ£Œλ˜μ—ˆμŠ΅λ‹ˆλ‹€.");
    model.addAttribute("searchUrl", "/board/post/" + id);

    return "message";
}

μˆ˜μ •μ΄ ν•„μš”ν•œ νŒŒμΌμ„ λͺ¨λ‘ μˆ˜μ •μ„ ν–ˆλ‹€. 이제 싀행을 ν•΄μ„œ κΈ°λŠ₯이 잘 λ™μž‘ν•˜λŠ”μ§€ ν™•μΈν•΄λ³΄μž.


κ²Œμ‹œκΈ€ μž‘μ„± νŽ˜μ΄μ§€

μš°μ„  '파일 μ—…λ‘œλ“œ ν…ŒμŠ€νŠΈ' 글을 μž‘μ„±ν•˜λ©° μ•„λž˜μ˜ κ·€μ—¬μš΄ 강아지 사진을 μ²¨λΆ€ν•˜κ² λ‹€.

μš°μ„ μ μœΌλ‘œ DB에 잘 μ €μž₯이 λ˜μ—ˆλŠ”μ§€ 확인을 ν•΄λ³΄μž.


κ°€μž₯ μ•„λž˜κ°€ 방금 μž‘μ„±ν•œ 글인데 filenameκ³Ό filepath 등이 μ €μž₯된 것이 보인닀.

DBκΉŒμ§€ 확인을 ν–ˆμœΌλ‹ˆ 이제 files 폴더λ₯Ό ν™•μΈν•΄λ³΄μž. κ²Œμ‹œκΈ€μ˜ νŒŒμΌμ€ λͺ¨λ‘ files 폴더에 μ €μž₯을 ν•˜κ³  λΆˆλŸ¬μ˜¨λ‹€.

files 폴더λ₯Ό λ³΄λ‹ˆ DB의 filenameκ³Ό μΌμΉ˜ν•˜λŠ” 이름을 사진 사진이 폴더에 μ‘΄μž¬ν•œλ‹€.

λ§ˆμ§€λ§‰μœΌλ‘œ κ²Œμ‹œνŒ 상세 νŽ˜μ΄μ§€μ—μ„œ 확인을 ν•΄λ³΄μž.

κ²Œμ‹œκΈ€ 상세 νŽ˜μ΄μ§€

κ²Œμ‹œκΈ€ 상세 νŽ˜μ΄μ§€μ΄λ‹€. 파일 μ—…λ‘œλ“œ κΈ°λŠ₯이 μ •μƒμ μœΌλ‘œ κ΅¬ν˜„μ΄ λ˜μ—ˆλ‹€λ©΄ '사진'을 ν΄λ¦­ν–ˆμ„ λ•Œ κ·€μ—¬μš΄ 강아지가 λ³΄μ—¬μ•Όν•œλ‹€. 이제 클릭 해보겠닀.


μ„±κ³΅μ μœΌλ‘œ 강아지 사진이 보인닀! 파일 μ—…λ‘œλ“œ κΈ°λŠ₯이 μ •μƒμ μœΌλ‘œ κ΅¬ν˜„λλ‹€.


μ—¬λŸ¬λͺ¨λ‘œ λΆ€μ‘±ν•œ 점이 λ§Žλ‹€.

  • 파일 λ‹€μš΄λ‘œλ“œ κΈ°λŠ₯이 μ—†λ‹€.

  • 사진을 보기 μœ„ν•΄μ„œλŠ” μ„œλ²„ μž¬μ‹€ν–‰μ΄ ν•„μš”ν•˜λ‹€.

  • 사진을 ν΄λ¦­ν•΄μ„œ λ³΄λŠ” 것이 μ•„λ‹Œ κ²Œμ‹œκΈ€ 상세 νŽ˜μ΄μ§€μ— 보이고 μ‹Άλ‹€.

  • μˆ˜μ • νŽ˜μ΄μ§€μ—μ„œ 사진 μˆ˜μ •μ΄ λΆˆκ°€ν•˜λ‹€.

μœ„μ— 적은 κ²ƒμ²˜λŸΌ 아직 λΆ€μ‘±ν•œ 것이 λ§Žλ‹€. 이 뢀뢄듀은 μ°¨ν›„ ν•˜λ‚˜ν•˜λ‚˜ μˆ˜μ •ν•  κ³„νšμ΄λ‹€.


λ‹€μŒ 글은 μˆ˜μ • νŽ˜μ΄μ§€μ—μ„œ 사진 μˆ˜μ •μ΄ κ°€λŠ₯ν•˜λ„λ‘ λ³€κ²½ν•˜λŠ” 글을 μž‘μ„±ν•˜κ² μŠ΅λ‹ˆλ‹€. κΈ€μ—μ„œ λΆ€μ‘±ν•œ 뢀뢄이 λ§Žμ„ 것 κ°™μŠ΅λ‹ˆλ‹€. λͺ¨λ“  지적을 ν™˜μ˜ν•©λ‹ˆλ‹€!!

더 μžμ„Έν•œ μ½”λ“œλŠ” κΉƒν—ˆλΈŒλ₯Ό μ°Έκ³ ν•΄μ£Όμ„Έμš”!

κΉƒν—ˆλΈŒ: https://github.com/pp8817/ToyProjectBoard

profile
μŠ€ν”„λ§ λ°±μ—”λ“œλ₯Ό 곡뢀쀑인 λŒ€ν•™μƒμž…λ‹ˆλ‹€!

0개의 λŒ“κΈ€