본문 바로가기
Spring

[Spring] 파일 업로드(3) 한 개의 첨부파일 업로드(2)

by moca7 2024. 10. 24.

 

 

ㅁ 화면에서 업로드한 첨부 파일이 내가 지정한 경로에 내가 지정한 이름으로 저장은 되었다.

- 이제 db에 insert한다.

 

 

ㅁ 현재 게시글 등록 요청이라는 하나의 기능이다. board에도 attachment에도 isnert를 해야 한다.

- 하나의 기능에 2개의 sql문을 실행해야 한다.

- 하나의 기능이기 때문에 service는 하나다. 실행할 sql문은 2개니까 2개의 dao를 호출해야 한다.

 

 

ㅁ DB

 

 

 

ㅁ board가 부모고 attachment가 자식이라 board에 먼저 insert를 해야 한다.

- attachment의 ref_board_no 컬럼은 board의 board_no를 참조하고 있다. (외래키 제약조건)

 

 

 

ㅁ 쿼리를 미리 생각해본다

 

- mapper 파일 안에 세미콜론은 없어야 한다.

 

 

 

 

ㅁ board-mapper.xml

 

 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="boardMapper">
 
  <insert id="insertBoard">
    insert
      into board
      (
        board_no
      , board_title
      , board_content
      )
      values
      (
        seq_bno.nextval
      , #{boardTitle}
      , #{boardContent}
      )
  </insert>
 
 
 
  <insert id="insertAttach">
    insert
      into attachment
      (
        file_no
      , file_path
      , original_name
      , filesystem_name
      , ref_board_no
      )
      values
      (
        seq_fno.nextval
      , #{filePath}
      , #{originalName}
      , #{filesystemName}
      , seq_bno.currval
      )
  </insert>
 
</mapper>
 

 

- mapper 파일 안에 세미콜론은 없어야 한다.

 

 

 

 

 

 

ㅁ BoardController

 

 
package com.br.file.controller;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.multipart.MultipartFile;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;
import com.br.file.service.BoardService;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/board")
@RequiredArgsConstructor
@Controller
public class BoardController {

  private final BoardService boardService;
 
  @PostMapping("/insert1.do")
  public String insertOneFileBoard(BoardDto board, MultipartFile uploadFile) {
   
    log.debug("board: {}", board);      // 잘 담겼는지 확인용 로그 출력
    log.debug("attach: {}", uploadFile);  // 잘 담겼는지 확인용 로그 출력
   
   
    AttachDto attach = null;
   
    if(uploadFile != null && !uploadFile.isEmpty()) { // 첨부파일이 존재할 경우 => 업로드
      // 전달된 파일 업로드 처리
     
      // (1) 업로드할 폴더 (/upload/yyyyMMdd)
      String filePath = "/upload/" + new SimpleDateFormat("yyyyMMdd").format(new Date());
     
      File filePathDir = new File(filePath);
      if(!filePathDir.exists()) {   // 해당 경로의 폴더가 존재하지 않을 경우
        filePathDir.mkdirs();   // 해당 폴더 만들기
      }
     
     
      // (2) 파일명 수정 작업
      String originalFilename = uploadFile.getOriginalFilename(); // "xxxxx.jpg" | "xxxx.tar.gz" (파일 확장자가 2단계로 된 확장자도 있다)
     
      // 원본명으로부터 확장자 추출하기
      String originalExt = originalFilename.endsWith(".tar.gz") ? ".tar.gz"
                                    : originalFilename.substring(originalFilename.lastIndexOf("."));
     
      String filesystemName = UUID.randomUUID().toString().replace("-", "") + originalExt;
     
     
      // (3) 업로드 (폴더에 파일 저장)
      try {
        uploadFile.transferTo(new File(filePathDir, filesystemName));
      } catch (IllegalStateException e) {
        e.printStackTrace();
      } catch (IOException e) {
        e.printStackTrace();
      }
       
     
      // (4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅
      attach = AttachDto.builder()
                .filePath(filePath)
                .originalName(originalFilename)
                .filesystemName(filesystemName)
                .build();
     
    }
   


    int result = boardService.insertOneFileBoard(board, attach);
   
    if(result > 0) {
      log.debug("게시글 등록 성공");
    }else {
      log.debug("게시글 등록 실패");
    }

    return "redirect:/"; // 성공이든 실패든 메인페이지 요청
   
  }
}
 

 

 

- 추가한 부분 : AttachDto attach 변수 선언, (4), if문 아래 부분.

 

 

 

- 게시글 데이터는 BoardDto에, 첨부파일 데이터는 AttachDto에 담아서 넘기면서 호출을 하면 된다.

- AttachDto attach 변수를 선언하고 null로 초기화 했다.

첨부파일은 없을 수도 있다. 없으면 null인채로 넘길 예정이다.

 

 

(4) db에 기록할 정보를 자바 객체(AttachDto)에 세팅

- 넘어온 첨부파일이 있을 때만 진행한다.

- 원래 방식이었다면 매개변수 생성자를 그때마다 만들어야 했다.

내가 원하는 필드만 골라서 값을 담을 수 있다.

일일이 매개변수 생성자를 만들 필요 없고 매개변수의 개수와 타입이 중복될 일도 없다.

이제부턴 builder 패턴을 이용한다.

롬복의 @Builder 어노테이션을 붙여서 사용 가능하다.

- build()까지 호출하면 최종적으로 AttachDto 객체가 반환된다.

 

- 이제 if 블록을 나온다.

넘어온 첨부파일이 있든 없든 서비스 호출을 해야 한다.

 

- 게시글 등록이 성공이든 실패든 메인페이지를 재요청한다.

 

 

 

 

ㅁ BoardServiceImpl

 

 
package com.br.file.service;

import java.util.List;

import org.springframework.stereotype.Service;

import com.br.file.dao.BoardDao;
import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Service
public class BoardServiceImpl implements BoardService {

  private final BoardDao boardDao;

  @Override
  public int insertOneFileBoard(BoardDto board, AttachDto attach) {
   
   
    int result = boardDao.insertBoard(board);
   
    if(result > 0 && attach != null) {
      result = boardDao.insertAttach(attach);
    }
   
   
    return result;
  }

  @Override
  public int insertManyFileBoard(BoardDto board, List<AttachDto> list) {
    return 0;
  }

  @Override
  public List<AttachDto> selectAttachList() {
    return null;
  }
 
}

 

 

 

ㅁ BoardDao

 

 
package com.br.file.dao;

import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.stereotype.Repository;

import com.br.file.dto.AttachDto;
import com.br.file.dto.BoardDto;

import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Repository
public class BoardDao {

  private final SqlSessionTemplate sqlSession;
 
 
  public int insertBoard(BoardDto board) {
    return sqlSession.insert("boardMapper.insertBoard", board);
  }
 
  public int insertAttach(AttachDto attach) {
    return sqlSession.insert("boardMapper.insertAttach", attach);
  }
 
 
}

 

 

 

ㅁ 트랜잭션 처리

- aop 처리로 rollback 되게끔 해놨다. 

- root-context.xml에 트랜잭션 처리용 aop를 위한 구문을 작성했었다.

 

 

 

ㅁ 실행

- 첫번째로 제목, 내용만 입력하고 등록해본다.

- 두번째로 제목, 내용, 첨부파일을 선택하고 등록해본다.