Notice
Recent Posts
Recent Comments
Link
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | 5 | 6 | |
| 7 | 8 | 9 | 10 | 11 | 12 | 13 |
| 14 | 15 | 16 | 17 | 18 | 19 | 20 |
| 21 | 22 | 23 | 24 | 25 | 26 | 27 |
| 28 | 29 | 30 |
Tags
- 접근제어지시자
- java변수
- 메서드
- 시스템 환경 변수 편집
- 자바 멀티스레딩
- Java
- 포함관계
- Thread
- this예약어
- 연관관계
- OPP개념
- JAVA객체지향
- 형 변환
- 컴파일
- continue문
- break문
- 인텔리제이 기초 설정
- IntelliJ IDEA
- Java데이터 타입
- for문
- 집합관계
- 인텔리제이 한글 깨짐 해결법
- JAVA기초
- multi-threading
- 생성자
- 상수
- 메서드 오버로딩
- 반복문
- While
- function
Archives
- Today
- Total
최원종의 개발 블로그
V10-5 운영체제 별 경로 설정 본문
5단계 운영체제 별 경로 설정
FileUtil 핵심 코드 분석
public static final String IMAGES_DIR =
Paths.get(System.getProperty("user.home"), "blog_uploads").toString();
JVM 시스템 프로퍼티에서 사용자 홈 디렉토리를 동적으로 긁어온 뒤 blog_uploads 폴더를 결합하는 방식을 채택함으로써,
윈도우에서는 C:\Users\유저명\blog_uploads,
맥에서는 /Users/유저명/blog_uploads로 자동 가공되는 크로스 플랫폼 설계
FileUtil 코드⬇️
더보기
package com.tenco.blog._core.util;
import com.tenco.blog._core.errors.Exception400;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.UUID;
// IoC 안함 (파일 기능 처리에만 동작할 수 있도록 static 메서드로 구현할 예정)
public class FileUtil {
// 업로드될 파일 경로를 미지 상수로 지정
// System.getProperty("user.home")을 사용해서 OS 상관없이
// 사용자 홈 경로를 동적으로 설정해서 가져 옴
// 예) window : C:\Users\사용명\blog_uploads
// 예) Mac : /Users/사용자명/blog_uploads
public static final String IMAGES_DIR = Paths.get(System
.getProperty("user.home"), "blog_uploads").toString();
// 1. 파일 저장 하는 기능
public static String saveFile(MultipartFile file, String uploadDir) throws IOException {
// 1단계 : 파일 유효성 검사 - 파일이 없거나 크기가 0이면 오류
if (file == null || file.isEmpty()) {
return null; // 프로필 이미지 업로드는 선택 사항 임.
}
// 2단계 : 파일 업로드 경로 생성 (존재 여부 확인)
// Path : 파일 시스템 경로를 나타내는 객체
// Path.get() : 문자열 경로를 Path 객체로 변환해주는 객체
Path uploadPath = Paths.get(IMAGES_DIR);
// Files.exists() : 파일/디텍토리 존재 여부 확인
if(Files.exists(uploadPath) == false) {
// 현재 서버 컴퓨터에 images/* 없는 상태
Files.createDirectories(uploadPath); // 상위 폴더까지 자동 생성 해 줌
}
// 3단계 : 원본 파일 이름 가져오기
String originalFilename = file.getOriginalFilename();
if (originalFilename == null || originalFilename.isBlank()) {
throw new Exception400("파일명이 없습니다");
}
// 4단계 : UUID를 사용한 고유 파일명 생성
String uuid = UUID.randomUUID().toString(); // 난수 발생
String savedFileName = uuid + "_" + originalFilename;
// 예) "12334123-123e-123_a.png 파일명으로 재 생성 됨.
// 5단계 : 메모리상에 존재하는 파일 데이터를 로컬 컴퓨터(디스크)에 저장
// 5.1 - 파일폴더경로 + 재생성한파일이름 ---> 정확한 위치에 파일이 생성 됨
// 예 : images/123-2322-123_a.png
Path filePath = uploadPath.resolve(savedFileName);
Files.copy(file.getInputStream(), filePath);
return savedFileName;
}
// 2. 파일 삭제 하는 기능
public static void deleteFile(String fileName, String uploadDir) throws IOException {
if (fileName == null || fileName.isEmpty()) {
return;
}
// Path -> C://upload/xxx_a.png
Path filePath = Paths.get(uploadDir, fileName);
if(Files.exists(filePath)) {
// 정확한 폴더 경로 존재 확인, 파일명 기준으로 파일이 존재 한다면
Files.delete(filePath); // 실제 폴더에서 파일 삭제 됨.
}
}
// 3. 편의 기능 만들 예정 (이미지 파일이 맞는지 확인)
public static boolean isImageFile(MultipartFile file) {
if (file == null || file.isEmpty()) {
return false;
}
// pdf, hwp <-- 막아 줘야 한다.
String contentType = file.getContentType(); // image/png, image/jpg, application/pdf
boolean isImage = contentType.startsWith("image/");
return isImage;
}
}
WebMvcConfig (addResourceHandlers 수정)
수정된 핵심 코드
FileUtil과 WebMvcConfig의 완전한 물리 경로 동기화
String externalPath = Paths.get(FileUtil.IMAGES_DIR).toString();
registry.addResourceHandler("/images/**")
// file: 추가 하기
.addResourceLocations("file:" + externalPath);
이유: 이전 코드의 문제점은 파일 저장 유틸리티가 바라보는 실제 폴더(C:\\upload)와
설정 파일이 바라보는 폴더 위치를 개발자가 각각 수동으로 짝을 맞춰줘야 했기 때문에
한쪽을 바꾸고 다른 쪽을 놓치면 화면에 엑스박스가 뜨는 현상이 빈번함.
수정된 코드는 FileUtil.IMAGES_DIR 상수를 그대로 긁어와
접두사인 file:과 동적으로 문자열 결합을 수행함.
이로써 파일 저장 장소가 맥 환경이나 특정 서버 환경에 맞추어
어디로 바뀌든 간에 웹 가상화 리소스 위치가 자동으로 연동되어
추적되는 강력한 유지보수성을 확보.
WebMvcConfig 코드 ⬇️
더보기
package com.tenco.blog._core.config;
import com.tenco.blog._core.interceptor.LoginInterceptor;
import com.tenco.blog._core.interceptor.SessionInterceptor;
import com.tenco.blog._core.util.FileUtil;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import java.nio.file.Paths;
// 자바 코드로 스프링 부트 설정 파일을 다둘 수 있다.
// @Component
@Configuration // IoC 대상 - 하나 이상의 IoC 처리를 하고 싶을 때 사용 한다.
public class WebMvcConfig implements WebMvcConfigurer {
@Autowired // DI 처리
private LoginInterceptor loginInterceptor;
@Autowired // DI 처리
private SessionInterceptor sessionInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
// 화면에 SessionUser 정보를 내려줄 사용 됨.
registry.addInterceptor(sessionInterceptor)
.addPathPatterns("/**"); // 모든 URL 요청서 동작 함
// 인증 처리 인터셉터 동작 함
registry.addInterceptor(loginInterceptor)
.addPathPatterns("/board/**", "/user/**", "/reply/**")
.excludePathPatterns(
// 로그인 관련(인증이 필요 없는 페이지)
"/login-form", // 로그인 화면 요청 시
"/join-form", // 회원 가입 화면 요청 시
"/logout", // 로그아웃
// 게시글 조회 관련 (인증 없이도 볼 수 있는 페이지)
"/board/list", // 게시글 목록 화면 요청 시
"/" , // 메인 페이지
"/index" , // 메인 페이지
"/board/{id:\\d+}", // 게시글 상세보기( 숫자 ID만 허용)
// 정적 리소스 (CSS, JS, 이미지 등)
"/css/**", // CSS 파일 제외
"/js/**", // JS 파일 제외
"/images/**", // 이미지 파일 제외
"/favicon.ico", // 파비콘 제외
// H2 데이터베시스 콘솔( 개발 환경용)
"/h2-console/**" // H2 콘솔 접근
);
}
// 정적 리소스 핸들러 설정
// 외부 사용자가 내 서버 컴퓨터에 특정 경로를 바로 확인을 할 수 있게 한다면
// 보안상 취약 할 수 있습니다. ( 사용자에게는 가짜 경로를 보여주고 내부에서는
// 실정 경로를 찾을 수 있도록 처리 하는 기법(보안상)
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
String externalPath = Paths.get(FileUtil.IMAGES_DIR).toString();
registry.addResourceHandler("/images/**")
// file: 추가 하기
.addResourceLocations("file:" + externalPath);
}
}
yml 파일 수정 코드⬇️
더보기
server:
servlet:
encoding:
charset: utf-8
force: true
# 파일 업로드 설정
multipart:
enabled: true # 파일 업로드 활성화
max-file-size: 50MB # 단일 파일 최대 크기 설정 (1MB)
max-request-size: 100MB # 전체 요청(Request) 최대 크기 설정
port: 8080
logging:
level:
root: INFO #모든 라이브러리는 INFO 이상만 출력
com.tenco: DEBUG # 내 프로젝트는 DEBUG 이상 모두 출력
spring:
mustache:
servlet:
# 머스태치 템플릿 엔진에서 request 객체와 세션 객체에 접근할 수 있도록 허용하는 설정 추가
expose-session-attributes: false
expose-request-attributes: true
#데이터베이스 연결 설정 (MySQL)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/myblog?serverTimezone=Asia/Seoul
username: root
password: root
# driver-class-name: org.h2.Driver
# url: jdbc:h2:mem:test
# username: sa
# password:
h2:
console:
enabled: true
# 초기 데이터 설정
# SQL 초기화 설정
sql:
init:
#mode: always # 외부 DB(MySQL)에서도 실행되도록 설정
mode: never # 절대 실행 안됨
data-locations:
- classpath:db/data.sql
jpa:
# Open Session In View -> false 설정
# true (기본 값) -> 뷰 렌더링까지 DB 세션 유지 됨(LAZY 로딩 가능)
# false : 트랜잭션 종료시 DB 세션 종료 ( Controller, View LAZY 로딩 불가)
# false 사용시 Service 필요한 데이터를 모두 조회하고 응답시 DTO 설계 방식으로 처리 한다.
open-in-view: false
hibernate:
# create 애플리케이션 시작시 테이블 새로 생성
# 기존 데이터는 모드 삭제됨 (개발용)
ddl-auto: update
#SQL 쿼리를 콘솔에 출력 (개발용)
show-sql: true
properties:
hibernate:
# SQL 쿼리를 보기 좋게 포맷팅
format_sql: true
# N + 1 문제를 완화 하기위한 완충 장치 설정
# 기본 WHERE 절에서 in 절(in 쿼리로 변경 해준다)
default_batch_fetch_size: 100
# data.sql 파일을 하이버네티트 초기화 이후에 실행
# data.sql 파일을 Hibernate 초기화 이후에 실행
defer-datasource-initialization: true
'Spring boot 입문' 카테고리의 다른 글
| V11 - 2 역할 기반 접근 제어 (0) | 2026.05.26 |
|---|---|
| V11 - 1 역할 기반 접근 제어 (0) | 2026.05.26 |
| V10-4 프로필 이미지 수정 하기 (0) | 2026.05.22 |
| V10-3 프로필 이미지 삭제 하기 (0) | 2026.05.22 |
| V10-2 프로필 이미지 출력과 정적 리소스 핸들러 처리 (0) | 2026.05.22 |
