💡 게시판 - 파일 업로드 2

2026. 7. 20. 21:34·Project/나의 작은 프로젝트

이 글은 Velog에서 이전한 글입니다. Velog 원문 보기

지난 💡게시판 - 파일 업로드에서 게시판의 여러가지 부족한 점을 이야기 했다.

  • *수정 페이지에서 사진 수정이 불가하다. *
  • 파일 다운로드 기능이 없다.
  • 사진을 보기 위해서는 서버 재실행이 필요하다.
  • 사진을 클릭해서 보는 것이 아닌 게시글 상세 페이지에 보이고 싶다.

등등이 있는데 오늘은 그 중 수정 페이지에서 사진 수정이 불가하다는 사항을 개선해보려고 한다.

Board

package com.studyweb.webboard.entity;

import lombok.Builder;
import lombok.Data;

import javax.persistence.*;

@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;


    //수정을 위한 생성자
    @Builder
    public Board(String title, String content, String author) {
        this.title = title;
        this.content = content;
        this.author = author;
    }

    public void update(String title, String content) {
        this.title = title;
        this.content = content;
    }

    //기본 생성자 추가!
    public Board() {

    }
}

Entity 클래스인 Board의 전체 코드이다.


BoardService 전체 로직

package com.studyweb.webboard.service;

import com.studyweb.webboard.entity.Board;
import com.studyweb.webboard.repository.BoardRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.List;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class BoardService {
    private final BoardRepository boardRepository;

    public Board findById(Integer id) {
        return boardRepository.findById(id).get();
    }

    public Page<Board> boardList(Pageable pageable) {
        return boardRepository.findAll(pageable);
    }

    public Page<Board> boardSearchList(String searchKeyword, Pageable pageable) {
        return boardRepository.findByTitleContaining(searchKeyword, pageable);
    }

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

        //저장할 경로를 지정
        String projectPath = getProjectPath();

        // 랜덤 식별자 생성
        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);

        boardRepository.save(board);
    }

    public void delete(Integer id) {
        boardRepository.deleteById(id);
    }

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

        Board board = boardRepository.findById(id).get();
        board.update(updateBoard.getTitle(), updateBoard.getContent());

        boardRepository.save(board);

    private static String getProjectPath() {
        return System.getProperty("user.dir") + "/src/main/resources/static/files";
    }


}

우선 위는 게시판의 기능들을 모아둔 BoardService의 전체 로직이다. 이번에는 update 메서드를 수정해볼 것이다.

우선 save 메서드에 있는 것처럼 update 메서드 또한 파일을 매개변수로 받고 filename, filepath 지정해주는 코드가 필요하다. 이는 save에 있는 코드를 그대로 쓰겠다.

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

        Board board = boardRepository.findById(id).get();
        board.update(updateBoard.getTitle(), updateBoard.getContent());

        //저장할 경로를 지정
        String projectPath = getProjectPath();

        // 랜덤 식별자 생성
        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);

        boardRepository.save(board);

    private static String getProjectPath() {
        return System.getProperty("user.dir") + "/src/main/resources/static/files";

코드를 추가해줬다. 여기서 중요한 부분이 있다.

게시글을 수정할 때는 2가지 상황이 존재한다

  • 파일을 수정하는 경우
  • 파일을 수정하지 않는 경우

수정을 할 때마다 파일을 수정한다면 괜찮겠지만 아니라면 문제가 생긴다.
현재의 코드는 게시글을 수정할 때 파일을 수정하지 않고 게시글을 수정하게 된다면 이전의 파일이 사라진다는 문제를 가지고 있다.

파일을 수정하는 경우, 파일을 수정하지 않는 경우를 조건문으로 구분하겠다.

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

        Board board = boardRepository.findById(id).get();
        board.update(updateBoard.getTitle(), updateBoard.getContent());

        if (file.isEmpty()) {

            boardRepository.save(board);

        } else {
            //저장할 경로를 지정
            String projectPath = getProjectPath();

            // 랜덤 식별자 생성
            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);

            boardRepository.save(board);
        }
    }

private static String getProjectPath() {
     return System.getProperty("user.dir") + "/src/main/resources/static/files";
    }

조건문을 추가해서 file.isEmpty()가 True인 경우 파일을 수정하지 않은 경우로 보고 이전의 파일을 그대로 사용하도록 했다.

이제 html을 수정하자.

boardUpdate.html - 수정 전

<body>
<div class="container">
  <h1>게시글 수정</h1>
  <form th:action="@{/board/post/update/{id}(id=${post.id})}" method="post">

    <div class="form-group">
      <label for="title">제목</label>
      <input type="text" id="title" name="title" th:value="${post.title}" required>
    </div>

    <div class="form-group">
      <label for="author">작성자</label>
      <input type="text" id="author" name="author" th:value="${post.author}" disabled>
    </div>

    <div class="form-group">
      <label for="content">내용</label>
      <textarea id="content" name="content" rows="8" required th:text="${post.content}"></textarea>
    </div>

    <button type="submit" class="btn">수정하기</button>
  </form>
</div>
</body>

수정 전의 boardUpdate.html 코드이다. 제목과 내용이 수정하는 input이 있고, 작성자는 disable을 추가해서 수정이 불가하게 작성했다. (

GPT가 해줬다.

)

이제 수정을 해보자.

boardUpdate.html - 수정 후

<body>
<div class="container">
  <h1>게시글 수정</h1>

  <!--enctype="multipart/form-data" 추가-->
  <form th:action="@{/board/post/update/{id}(id=${post.id})}" method="post" enctype="multipart/form-data">

    <div class="form-group">
      <label for="title">제목</label>
      <input type="text" id="title" name="title" th:value="${post.title}" required>
    </div>

    <div class="form-group">
      <label for="author">작성자</label>
      <input type="text" id="author" name="author" th:value="${post.author}" disabled>
    </div>

    <div class="form-group">
      <label for="content">내용</label>
      <textarea id="content" name="content" rows="8" th:text="${post.content}" required></textarea>

      <!--파일 업로드 부분 추가-->
      <a th:href="@{${post.filepath}}">사진</a>
      <input type="file" name="file">
      <!--파일 업로드 부분 끝-->

    </div>

    <button type="submit" class="btn">수정하기</button>
  </form>
</div>
</body>

큰 변화는 없다.
form 태그에 enctype="multipart/form-data"를 추가했다. 단순히 텍스트를 변경할 때는 필요 없지만 파일 수정이 추가되면서 여러 형태의 데이터를 변경함에 따라서 타입을 Multipart/form-data로 해야한다.

<!--게시글 작성 때 등록한 파일을 보는 코드-->
<a th:href="@{${post.filepath}}">사진</a>

<!--수정할 파일을 올리는 코드-->
<input type="file" name="file">

게시글을 작성할 때 등록했던 파일을 보는 코드와 수정할 파일을 업로드 하는 코드를 추가했다.

여기까지하면 디자인은 별로지만 기능의 추가는 끝이났다.


여전히 부족한 점이 많다.

  • *수정 페이지에서 사진 수정이 불가하다. *
  • 파일 다운로드 기능이 없다.
  • 사진을 보기 위해서는 서버 재실행이 필요하다.
  • 사진을 클릭해서 보는 것이 아닌 게시글 상세 페이지에 보이고 싶다.

위의 내용과 더불어

  • 사진을 클릭해서 보는 것이 아닌 바로 보이게 하고 싶다.
  • 업로드한 파일을 삭제하게 만들고 싶다.
  • 파일 업로드 디자인 수정

등등 파일 업로드 부분을 제외하고 다른 부분에서도 수정해야 할 부분이 많다.

기능을 추가하고, 수정하면서 부족함 점이 끝도 없이 보이다 보니 계속해서 배워나가는 시간인 것 같다. 언젠가 다른 사람들에게 자랑스럽게 보여주고 배포할 수 있는 프로젝트를 만들 때까지 열심히 달려나가야겠다!


글에서 부족한 부분이 많을 것 같습니다. 모든 지적을 환영합니다!!

더 자세한 코드는 깃허브를 참고해주세요!

깃허브: https://github.com/pp8817/ToyProjectBoard

'Project > 나의 작은 프로젝트' 카테고리의 다른 글

💡 게시판 - 파일 다운로드  (1) 2026.07.20
💡 게시판 - 파일 업로드 3(개선)  (0) 2026.07.20
💡 게시판 - 파일 업로드  (0) 2026.07.20
💡 게시판 - 검색 기능  (0) 2026.07.20
💡 게시판 - 페이징 처리  (0) 2026.07.20
'Project/나의 작은 프로젝트' 카테고리의 다른 글
  • 💡 게시판 - 파일 다운로드
  • 💡 게시판 - 파일 업로드 3(개선)
  • 💡 게시판 - 파일 업로드
  • 💡 게시판 - 검색 기능
pp8817
pp8817
공부한 내용, 개발 관련 지식, 트러블 슈팅 등을 기록합니다. 이전 블로그: https://velog.io/@pp8817/posts
  • pp8817
    끄적이는 개발 log
    pp8817
  • 전체
    오늘
    어제
    • 분류 전체보기 (273)
      • Project (71)
        • 척척학사 (29)
        • Book (23)
        • Saynow (3)
        • YAPP 27기 (4)
        • 나의 작은 프로젝트 (10)
        • Landit (2)
      • Backend (88)
        • Spring (13)
        • Spring MVC (13)
        • Spring Security (4)
        • JPA (26)
        • Database (19)
        • HTTP·Web (13)
        • Architecture (0)
      • Language·CS (59)
        • Java·Kotlin (5)
        • CS Interview (17)
        • Backend Interview (5)
        • Concepts (32)
      • Algorithm (25)
        • Algorithm (24)
        • 소마 알고리즘 스터디 (1)
      • Infra (8)
      • Troubleshooting (11)
      • Retrospective (6)
      • Etc (5)
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • 링크

  • 공지사항

  • 인기 글

  • 태그

    개념 정리!
    Spring MVC
    Book
    HTTP WEB 기본 지식
    척척학사
    object
    트러블슈팅
    Python
    interview
    Algorithm
    java
    CS Interview
    게시판
    Project
    Spring
    jpa
    OS
    BOJ
    나의 작은 프로젝트
    http
  • 최근 댓글

  • 최근 글

  • hELLO· Designed By정상우.v4.10.6
pp8817
💡 게시판 - 파일 업로드 2
상단으로

티스토리툴바