
CREATE TABLE "MEMBER" (
"MEMBER_NO" NUMBER NOT NULL,
"MEMBER_EMAIL" NVARCHAR2(50) NOT NULL,
"MEMBER_PW" NVARCHAR2(100) NOT NULL,
"MEMBER_NICKNAME" NVARCHAR2(10) NOT NULL,
"MEMBER_TEL" CHAR(11) NOT NULL,
"MEMBER_ADDRESS" NVARCHAR2(300) NULL,
"PROFILE_IMG" VARCHAR2(300) NULL,
"ENROLL_DATE" DATE DEFAULT SYSDATE NOT NULL,
"MEMBER_DEL_FL" CHAR(1) DEFAULT 'N' NOT NULL,
"AUTHORITY" NUMBER DEFAULT 1 NOT NULL
);
⇒ 사용자는 바로 삭제하는 것이 아닌, MEMBER_DEL_FL(Delete Flag)를 수정함으로써,
스케줄러를 통해서 1달정도? 데이터를 갖고있다가 삭제를 해준다.
<update id="secession">
UPDATE MEMBER
SET MEMBER_DEL_FL = "Y"
WHERE MEMBER_NO = #{memberNo}
</update>
**@SessionAttributes({"loginMember"})** // SessionStatus 사용하기 위함
@Controller
@RequestMapping("myPage")
@Slf4j
public class MyPageController {
public String secession(@RequestParam("memberPw") String memberPw,
@SessionAttribute("loginMember") Member loginMember, SessionStatus status, RedirectAttributes ra) {
status.setComplete(); // 세션 비우기 (로그아웃 상태 변경)
.
.
}
}
⇒ status는 @SessionAttributes와 함께 사용해야 한다.
<input type="file" name="uploadFile" />

[form태그의 enctype 속성]
enctype 이란??
- 데이터를 서버로 전송할 때 데이터의 형태(인코딩)를 지정하는 속성
- 이미지를 제출할 때 처리하는 타입
* application/x-www-form-urlencoded (기본값)
- URL 인코딩된 "문자열" 로 서버에 제출
* multipart/form-data (무조건 POST에서만 동작)
- 제출되는 여러 데이터 타입에 맞춰서 인코딩하여 제출
문자열 -> String
숫자 -> String -> int/double(Spring이 처리)
파일 -> 2진 데이터
* text/plain
- 텍스트로 서버에 제출

⇒ c드라이브에 uploadFiles에 클라이언트가 전달한 이미지를 저장할 것!
클라이언트가 프로필 업로드 요청 → c:/uploadFiles/profile/…에 존재할 것
img 태그 src “” 안에 들어가는 것이 이미지를 보여줘야되는 요청 경로
하지만, c:/uploadFiles/profile/.. 경로를 그대로 쓰면 안된다.
- 프로필 이미지를 화면에서 조회하는 요청은 /myPage/profile/마동석.jpg로 시작하는 요청으로 매핑해준다
- → img src=”/myPage/profile/마동석.jpg”
- /myPage/profile/ 이 요청을 c:/uploadFiles/profile/ 이 경로와 연결을 해서, 해당 c드라이브에서 찾아서 보여주면 된다.
- ⇒ 경로 매핑과정 필요
- 사용자가 게시판 이미지를 업로드 → c:/uploadFiles/board/에 저장
- img src = ”/board/image/짱구.png” 게시판 이미지를 조회하고 싶다는 요청
- /board/image/짱구.png ↔ c:/uploadFiles/board/ 경로와 연결을 해줘야 한다.
⇒ 해당 내용들을 FileConfig에 작성해줘야 한다.
⇒ Multipart Resolver 정의
⇒ config.properties에 작성하여 보안 강화
@Value("${spring.servlet.miltipart.file-size-threshold}")
# (참고)
# 1MB = 1024KB
# 1KB = 1024B
# 파일을 디스크에 쓸 때까지의 임계값
# 기본값 : 0B
# 50MB로 임계값을 변경
# => 임계값을 초과하면 그때부터 디스크에 업로드
# -> 업로드되는 파일의 크기 50MB 까지는 메모리에 임시로 저장하다가
# 초과시 디스크(HDD, SDD)에 저장
# 클라이언트가 이미지를 올리겠다고 요청을 보내면, 서버 컴퓨터 하드에 저장할 지 말지를 결정해야함
# 클라이언트가 요청을 보냈다고 해서 하드에 바로 넣어버리면 서버 컴퓨터에 부하가 일어남
# RAM에 저장하다가 임계값이 넘어버리면 그때 HDD, SDD에 저장
# 임계값은 바이트 단위로 저장
spring.servlet.miltipart.file-size-threshold=52428800
# file-size-threshold = 파일사이즈의 임계값을 의미 => 52428800 = 50MB
# 임계값이 초과가 되면, 디스크에 저장 -> 어느 경로에 저장할 지 정할 수 있음
# 즉, 파일의 임시 저장 경로를 지정
spring.servlet.multipart.location=C:/uploadFiles/temp/
# HTTP 요청당 최대 크기 지정
# 기본값 : 10MB
# => 50MB로 변경
spring.servlet.multipart.max-request-size=52428800
# 개별 파일당 최대 크기
# 기본값 : 1MB
# 10MB로 변경
spring.servlet.multipart.max-file-size=10485760
package edu.kh.project.common.config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.MultipartConfigFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.util.unit.DataSize;
import org.springframework.web.multipart.MultipartResolver;
import org.springframework.web.multipart.support.StandardServletMultipartResolver;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import jakarta.servlet.MultipartConfigElement;
@Configuration
@PropertySource("classpath:config.properties")
// 외부에 있는 properties를 가져오는 것
public class FileConfig implements WebMvcConfigurer {
// WebMvcConfigurer : Spring MVC 프레임워크에서 제공하는
// 인터페이스 중 하나로, 스프링 구성을 커스터마이징하고
// 확장하기 위한 메서드를 제공함.
// 주로 웹 애플리케이션의 설정을 조정하거나 추가하는데 사용됨
// 파일 업로드 임계값
// @Value : properties에 매핑된 키를 통해 value를 가져오는 어노테이션
@Value("${spring.servlet.miltipart.file-size-threshold}")
private long fileSizeThreshold; // 52428800
// 임계값 초과시 파일의 임시 저장경로
@Value("${spring.servlet.multipart.location}")
private String location; // C:/uploadFiles/temp/
// 요청당 파일 최대 크기
@Value("${spring.servlet.multipart.max-request-size}")
private long maxRequestSize; // 52428800
// 개별 파일당 최대 크기
@Value("${spring.servlet.multipart.max-file-size}")
private long maxFileSize; // 10485760
// 요청 주소에 따라
// 서버 컴퓨터의 어떤 경로에 접근할 지 설정하는 메서드
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// ResourceHandlerRegistry :
// Spring MVC에서 정적 리소스(이미지, CSS, JS 등)
// 요청을 처리하기 위해 사용하는 클래스
// URL 요청 패턴을 서버의 실제 파일 경로와 연결하여
// 클라이언트가 특정 경로로 정적파일에 접근할 수 있도록 설정
// 어떤 요청이 왔을 때 실행될지 정하는 것
registry.addResourceHandler("/myPage/file/**").addResourceLocations("file:///C:/uploadFiles/test/");
// 즉, /myPage/files/에 요청이 오면 C:/uploadFiles/test와 연결하겠다는 의미
// -> 클라이언트가 /myPage/file/** 패턴으로 이미지를 요청할 때
// 서버 폴더 경로 중 C:/uploadFiles/test/로 연결하겠다.
}
@Bean
// MultipartResolver 설정
public MultipartConfigElement configElement() {
// MultipartConfigElement
// 파일 업로드를 처리하는데 사용되는 MultipartConfigElement를
// 구성을 하고 반환을 해주는 것(옵션 설정하는데 사용)
// 업로드 파일의 최대 크기, 임시 저장경로 등등을 설정할 수 있게 해주는 객체
MultipartConfigFactory factory = new MultipartConfigFactory();
// 파일 업로드 임계값
factory.setFileSizeThreshold(DataSize.ofBytes(fileSizeThreshold));
// long타입으로 byte를 넣어야 함
// 임시 저장 폴더 경로
factory.setLocation(location);
// HTTP 요청당 파일 최대 크기
factory.setMaxRequestSize(DataSize.ofBytes(maxFileSize));
// 개발 파일당 최대 크기
factory.setMaxFileSize(DataSize.ofBytes(maxFileSize));
return factory.createMultipartConfig();
}
// MultipartResolver 객체를 생성하여 Bean으로 등록
// -> 위에서 만든 MultipartConfigElement를 자동으로 이용할 것
@Bean
public MultipartResolver multipartResolver() {
// MultipartResolver : MultipartFile을 처리해주는 해결사
// -> MultipartResolver는 클라이언트로부터 받은 multipart 요청을 처리하고,
// 그 중 업로드된 파일을 추출하여 MultipartFile 객체로 제공하는 역할
StandardServletMultipartResolver multipartResolver = new StandardServletMultipartResolver();
return multipartResolver;
}
}
// 파일 테스트 화면으로 이동
@GetMapping("fileTest")
public String fileTest() {
return "myPage/myPage-fileTest";
}
.
.
@PostMapping("file/test1") // /myPage/file/test1 POST 요청 매핑
public String fileUpload1(@RequestParam("uploadFile") MultipartFile uploadFile, RedirectAttributes ra) {
// 파일이기 때문에, String 등이 아닌 MultipartFile 타입으로 정의한다
String path = null;
try {
path = service.fileUpload1(uploadFile);
// /myPage/file/파일명.jpg
// 파일이 실제로 서버 컴퓨터에 저장이 되어
// 웹에서 접근할 수 있는 경로가 반환되었을 때
if (path != null) {
ra.addFlashAttribute("path", path);
}
} catch (Exception e) {
e.printStackTrace();
log.info("파일 업로드 예제1 중 예외 발생");
}
return "redirect:/myPage/fileTest";
}
/**
* 파일 업로드 테스트1
*/
@Override
public String fileUpload1(MultipartFile uploadFile) throws Exception {
// uploadFile에 파일이 업로드 됐는지부터 확인
if (uploadFile.isEmpty()) {
// 업로드한 파일이 없을 경우(즉, 껍데기만 넘어온 경우)
return null;
}
// 업로드한 파일이 있을 경우
// C:/uploadFiles/test/파일명으로 서버에 저장
uploadFile.transferTo(new File("C:/uploadFiles/test/" + uploadFile.getOriginalFilename()));
// 하기와.jpg로 해당 경로에 저장하겠다는 의미
// 웹에서 해당 파일에 접근할 수 있는 경로를 만들어 반환
// 이미지가 최종 저장된 서버 컴퓨터상의 경로
// C:/uploadFiles/test/파일명
// 클라이언트가 브라우저에 해당 이미지를 보기위해 요청하는 경로
// <img src="경로">
// /myPage/file/파일명.jpg -> <img src="/myPage/file/파일명.jpg">
return "/myPage/file/" + uploadFile.getOriginalFilename();
}


⇒ 서버 컴퓨터인 C:/uploadFiles/test에 저장이 된것을 확인할 수 있다.