코딩항해기
[Team/붕어빵원정대(최프)] 동적 경로를 활용한 FileUtil 작업 본문
담당하고 있는 Util 작업 중 MultipartFile 진도를 나가게 되어 해당 방식을 코드에 적용시키며 발생한 고정 경로문제를 해결했다.
설계
설계는 간단하게 선언부와 기능을 요약해 작성했으며 해당 설계를 바탕으로 코드 흐름을 짜고 구현했다. (후술할 경로문제로 초기 설계에서 많은 설계 수정을 거친 최종 버전이다.)
문제 발생
외부 경로에 파일을 생성해 거기서 이미지를 저장하고 관리한다면 발생하지 않는 문제지만, 현재 이미지는 webapp 하위 uploads 폴더 아래에서 관리할 예정이므로 팀원 모두의 프로젝트 파일 경로가 다르다는 문제가 발생한다.
그렇기 때문에 이 상태 그대로 상단이나 config 파일에 경로를 작성하게 되면 팀원들마다 직접 경로를 맞춰 수정해야하고, 프로젝트 파일 폴더 경로가 바뀌는 날에는 대대적인 수정이 들어가야한다. 또한 git을 사용하고 있기 때문에 gitignore로 config파일을 지정해야하고, 중요한 업데이트가 있다면 수동으로 파일을 공유해야하는 문제가 생긴다. (이슈로 브랜치 따올 때도 마찬가지다.)
이러한 문제를 해결하기 위해 동적으로 해당 경로를 받아와 폴더를 지정하는 방식으로 구성하고 싶어졌다.
(view에서 사용하는 pageContext.request.contextPath 와 같은 기능을 구현하고 싶었다.)
[JSP] 내장 객체 (pageContext)
pageContext (PageContext)pageContext 객체는 JSP 기본 내장 객체 중 하나로 따로 선언하지 않아도 사용 가능하다. pageContext는 JSP 페이지에 대해 1:1로 연결된 객체로 JSP 하나 당 하나의 pageContext 객체가 생성
minibcake.tistory.com
여러 경로를 반환하는 메서드를 사용해봤지만 프로젝트 경로를 반환하는 메서드를 찾지 못했고, 대신 taget 폴더의 경로를 반환하는 메서드가 있어 해당 메서드를 활용하기로 결정했다.
해결
String projectTarget = servletContext.getRealPath("/"); //taget폴더 내의 해당 프로젝트 관련 폴더 경로 반환
log.info("log: projectTarget: {}", projectTarget);
//taget폴더 가기 이전까지의 경로를 추출해 webapp까지의 기본 경로 생성
String project = projectTarget.substring(0, projectTarget.lastIndexOf("target")) + "src\\main\\webapp\\";
log.info("log: project: {}", project);
taget 이전까지의 주소만 필요하므로 주소를 받아와 필요한 경로만 추출한다. 모든 파일은 webapp 하위에 저장될 예정이므로 경로를 만들 때 함께 추가했다.
그럼 프로젝트까지의 주소 + src부터 webapp까지의 경로가 더해져 원하는 경로를 만들 수 있게 됐다.
개발환경이 달라질 때마다 경로를 수정해야하는 문제가 해결됐다.
FileUtil
@Slf4j
public class FileUtil {
//UUID와 현재 시간으로 String 반환
public static String createFileName(){
log.info("log: createFileName start");
String fileName = LocalDate.now() + "+" + UUID.randomUUID(); //파일명 구성
log.info("log: file name [{}]", fileName);
log.info("log: createFileName end");
return fileName;
}
//해당 폴더 하위의 폴더와 파일 삭제
public static boolean deleteFileAndDirectory (File folder){
log.info("log: deleteFile - start");
//서버에 해당 경로의 폴더가 있다면
if(folder.exists()) {
File[] files = folder.listFiles(); //해당 폴더의 파일리스트 데이터
if(files != null) { //빈 폴더가 아니라면
for(File file : files) {
log.info("log: deleteFile - board image file delete file : [{}]", file);
if(file.isDirectory()){ //만약 파일이 아니라 폴더라면
log.info("log: deleteFile - {} is directory", file);
//재귀함수
deleteFileAndDirectory(file);
}
if(!file.delete()){ //해당 파일 삭제
//파일 삭제 실패 시
//개발자에게 안내
log.error("log: deleteFile - board image file delete fail!!!! file : [{}]", file.getPath());
}
}
if(!folder.delete()){
//폴더 삭제 실패 시 개발자에게 안내
log.error("log: deleteFile - board image folder delete fail!!!! folder : [{}]", folder.getPath());
return false;
}
}
}
else{
//해당 경로에 폴더가 존재하지 않음을 안내
log.error("log: deleteFile - no image folder error imagePath: [{}]", folder.getPath());
return false;
}
log.info("log: deleteFile - end / true");
return true;
}
//입력받은 경로에 입력받은 파일을 저장 후 성공하면 파일명, 실패하면 null값 반환
public static String insertFile (ServletContext servletContext, String path, MultipartFile file, String fileName) {
log.info("log: insertFile - start");
//각기 다른 작업환경에서도 원활하게 구동될 수 있도록 경로를 받아와 프로젝트 경로 추출 후 기본 경로 생성//
String projectTarget = servletContext.getRealPath("/"); //taget폴더 내의 해당 프로젝트 관련 폴더 경로 반환
log.info("log: projectTarget: {}", projectTarget);
//taget폴더 가기 이전까지의 경로를 추출해 webapp까지의 기본 경로 생성
String project = projectTarget.substring(0, projectTarget.lastIndexOf("target")) + "src\\main\\webapp\\";
log.info("log: project: {}", project);
//////////////////////////////////////////////////////////////////////////
File folder = new File(project+path); //완성된 경로
if(!folder.exists()) { //해당 경로가 존재하지 않는다면
if(!folder.mkdirs()){ //해당 경로에 필요한 폴더 생성
//생성 실패
log.error("log: insertFile - mkdirs Fail folder : [{}]", folder.getPath());
return null;
}
}
String extension = ""; //확장자, 만약 문자열에 .이 없다면 초기값
String originalFilename = file.getOriginalFilename();
if(originalFilename != null && originalFilename.contains(".")){ //.이 있는 문자열이라면
log.info("log: createFileName file contains '.'");
extension = originalFilename.substring(originalFilename.lastIndexOf("."));//확장자 추출
}
String newFileName = fileName + extension; //DB에 저장하는 경우를 위한 String 값
try {
file.transferTo(new File(folder.getPath() + newFileName)); //지정된 경로에 전달받은 이름, 추출한 확장자로 저장
} catch (IOException e) {
log.error("log: insertFile - file save fail");
return null;
}
log.info("log: insertFile - end / return String : [{}]", newFileName);
return newFileName;
}
}
'Project' 카테고리의 다른 글
[Team/붕어빵원정대(최프)] Model와 설계 조율 (2) (0) | 2024.10.24 |
---|---|
[Team/붕어빵원정대(최프)] View파트와 설계 조율 (2) (4) | 2024.10.23 |
[Team/붕어빵원정대(최프)] AOP 작업 (0) | 2024.10.21 |
[Team/붕어빵원정대(최프)] Model파트와 설계 조율 (0) | 2024.10.19 |
[Team/붕어빵원정대(최프)] View파트와 설계 조율 (0) | 2024.10.18 |