<select id="findById" parameterType="map" resultType="Member">
SELECT
<include refid="memberColumns" />
FROM member
WHERE id = #{id, jdbcType=BIGINT}
and member_id = #{memberId, jdbcType=VARCHAR}
</select>
✅ @Param 이용
✔ Controller
memberService.findById(Long id, String memberId)
✔ Service
memberMapper.findById(Long id, String memberId)
✔ Mapper
Member findById(@Param("id") Long id, @Param("memberId") Long memberId);
✔ 그런데 mapper interface에서 다음과 같은 경고메세지가 빨간줄과 함께 표시됩니다.
@Mapper
public interface InfoMapper {
String getInfo();
}
===============================================
Result type not match for select id="getInfo"
srcType: java. lang. String
targetType: java. lang. String
===============================================
분명 같은 타입인데도 말이죠.
MyBatis에서 resultType="String"을 지정하면 "Result Maps collection does not have a type handler for result object" 또는 비슷한 경고 메시지가 발생할 수 있습니다. 이는 MyBatis가 반환할 데이터 타입을 명확하게 매핑하는 과정에서 발생하는 경고입니다.
📝 해결 방법
✅ resultType="java.lang.String"으로 명시
✔ 경고가 단순한 매핑 이슈라면 resultType="java.lang.String"처럼 패키지명을 명확하게 적어주는 것만으로도 해결되는 경우가 있습니다.
select *
from 회원테이블
where member_id like '김' + '%'
✅ concat 함수를 이용하여 분리해야 합니다.
select *
from 회원테이블
where member_id like concat('김', '%')
✅ 이제 Mybatis에서 parameter로 검색어를 받아서 조회할 수 있습니다.
<select id="findByMember" parameterType="String" resultType="Member">
select member_id, member_name
from member
where member_name like concat(#{searchTxt},'%')
</select>
import java.time.LocalDate;
public class Main {
public static void main(String[] args) {
LocalDate today = LocalDate.now();
System.out.println("현재 날짜: " + today);
// 포맷 적용하기
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy/MM/dd");
String nowString = now.format(formatter);
System.out.println("포맷팅 날짜: " + nowString);
}
}
👉출력 결과
현재 날짜: 2025-03-04
포맷팅 날짜: 2025/03/04
✅LocalDateTime 사용 (날짜와 시간 필요할 때)
import java.time.LocalDateTime;
public class Main {
public static void main(String[] args) {
LocalDateTime now = LocalDateTime.now();
System.out.println("현재 날짜와 시간: " + now);
}
}
👉출력 결과
현재 날짜와 시간: 2025-03-04T14:30:45.123
✅ ZonedDateTime 사용 (시간대 포함)
import java.time.ZonedDateTime;
import java.time.ZoneId;
public class Main {
public static void main(String[] args) {
ZonedDateTime seoulTime = ZonedDateTime.now(ZoneId.of("Asia/Seoul"));
System.out.println("서울 시간: " + seoulTime);
}
}
👉출력 결과
서울 시간: 2025-03-04T14:30:45.123+09:00[Asia/Seoul]
✅ Date 클래스 사용 (구버전(버전 8 이전), 권장X)
import java.util.Date;
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println("현재 날짜와 시간: " + date);
}
}
📢 IntelliJ IDEA Community Edition에서 한글 언어팩을 사용하는 방법은 다음과 같습니다
✅ 플러그인 설치:
🟠 상단 메뉴에서 File > Settings(Windows/Linux) 또는 IntelliJ IDEA > Preferences(Mac)를 선택합니다. 🟠Settings 창에서 왼쪽 메뉴에서 Plugins를 클릭합니다. 🟠Marketplace 탭을 선택한 후, 검색창에 Korean Language Pack 또는 Korean을 입력하여 한글 언어팩을 검색합니다. 🟠검색 결과에서 Korean Language Pack 플러그인을 선택하고, Install 버튼을 클릭합니다. 🟠플러그인 설치가 완료되면, Restart 버튼을 눌러 IntelliJ IDEA를 재시작합니다.
✅언어 설정 (플러그인설치만으로 안되는 경우)
🟠상단 메뉴에서 File > Settings(Windows/Linux) 또는 IntelliJ IDEA > Preferences(Mac)를 선택합니다. 🟠 Settings 창에서 Appearance & Behavior > System Settings > Language and Region를 선택합니다. 🟠 Language를 Korean 한국어로 선택하고 IntelliJ IDEA를 재시작합니다
MultipartFile은 Spring Framework에서 제공하는 인터페이스로, HTTP 요청을 통해 업로드된 파일을 다룰 때 사용됩니다. multipart/form-data 타입의 요청을 처리할 수 있도록 설계되어 있으며, 이를 활용하면 이미지, 문서 등 다양한 파일을 서버로 업로드할 수 있습니다.
1️⃣ MultipartFile 기본 개념
Spring에서는 클라이언트가 파일을 업로드할 때 multipart/form-data 형식으로 요청을 보내야 합니다. 이때 Spring의 MultipartResolver가 요청을 해석하고, MultipartFile 객체를 생성하여 컨트롤러에서 이를 받을 수 있도록 합니다.
✅ MultipartFile 인터페이스 주요 메서드
메서드
설명
getName()
요청 파라미터의 이름 반환
getOriginalFilename()
업로드된 파일의 원본 이름 반환
getContentType()
파일의 MIME 타입 반환 (image/png, application/pdf 등)
getSize()
파일 크기(byte 단위) 반환
isEmpty()
파일이 비어있는지 확인
getBytes()
파일을 바이트 배열로 변환
getInputStream()
파일을 스트림 형태로 변환
transferTo(File dest)
파일을 지정한 위치로 저장
2️⃣ 하나의 파일 업로드 예제 (Spring Boot)
Spring Boot에서 MultipartFile을 이용하여 파일을 업로드하는 예제를 만들어 보겠습니다.
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
@RestController
@RequestMapping("/upload")
public class FileUploadController {
@PostMapping
public String uploadFile(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "파일이 비어 있습니다.";
}
// 파일 저장 경로 (예제: 로컬 디렉토리)
String filePath = "C:/upload/" + file.getOriginalFilename();
try {
file.transferTo(new File(filePath)); // 파일 저장
return "파일 업로드 성공: " + file.getOriginalFilename();
} catch (Exception e) {
return "파일 업로드 실패: " + e.getMessage();
}
}
}
📝파일 업로드 테스트 (Postman)
위의 컨트롤러를 실행한 후 Postman을 사용하여 파일 업로드를 테스트할 수 있습니다.
Postman 실행
POST 요청을 보낼 URL: http://localhost:8080/upload
Body → form-data 선택
key 값을 file로 설정 (컨트롤러에서 @RequestParam("file")로 받으므로)
Type을 File로 선택 후 업로드할 파일 추가
Send 버튼 클릭 → 응답 확인
💡 첨부파일 앞에 느낌표가 있다면 Working Directory를 설정해야 합니다.
🔧 Postman의 작업 디렉토리 설정 변경
Postman 실행
Settings(설정) → General(일반) 탭으로 이동
Working Directory(작업 디렉토리) 항목 찾기
"Change Directory"(디렉토리 변경) 버튼 클릭
업로드할 파일이 있는 폴더를 선택하여 작업 디렉토리로 설정
설정 저장 후 Postman 재시작
👉이렇게 하면 Postman이 해당 폴더 내 파일을 정상적으로 참조할 수 있습니다. (느낌표시가 사라짐~)
✔ @RequestParam("files") List<MultipartFile> files를 사용하여 여러 개의 파일을 받습니다. ✔ 반복문을 사용하여 각 파일을 저장합니다.
🔧 Postman 테스트 방법
POST 요청을 보낼 URL: http://localhost:8080/upload/multiple
Body → form-data 선택
key 값을 files로 설정
Type을 File로 선택 후 여러 개의 파일 추가
Send 버튼 클릭 → 응답 확인
👉 테스트 결과입니다.
4️⃣ 파일 크기 제한
application.properties파일에서 업로드할 파일 크기를 설정할 수 있습니다.
spring.servlet.multipart.enabled=true
spring.servlet.multipart.max-file-size=5MB # 개별 파일 크기 제한
spring.servlet.multipart.max-request-size=20MB # 전체 요청 크기 제한
5️⃣ 업로드 파일 확장자 제한 (예: 이미지만 허용)
파일 확장자를 검사하여 특정 유형의 파일만 업로드하도록 설정할 수도 있습니다.
@RestController
@RequestMapping("/limit")
public class LimitUploadController {
@PostMapping
public String limitUpload(@RequestParam("file") MultipartFile file) {
if (file.isEmpty()) {
return "파일이 없습니다.";
}
// 허용할 확장자 목록
List<String> allowedExtensions = List.of("jpg", "jpeg", "png", "gif");
// 파일 확장자 추출
String originalFilename = file.getOriginalFilename();
assert originalFilename != null;
String fileExtension = originalFilename.substring(originalFilename.lastIndexOf(".") + 1);
if (!allowedExtensions.contains(fileExtension.toLowerCase())) {
return "허용되지 않은 파일 형식입니다.";
}
try {
file.transferTo(new File("C:/upload/" + originalFilename));
return "파일 업로드 성공: " + originalFilename;
} catch (IOException e) {
return "파일 업로드 실패: " + e.getMessage();
}
}
}
version: '3' # 사용할 Docker Compose 파일의 버전 (v3)
services:
react: # 'react'라는 이름의 서비스 정의
build:
context: . # 현재 디렉터리를 빌드 컨텍스트로 사용
dockerfile: Dockerfile # React 개발을 위한 Dockerfile 지정 (Dockerfile)
# 포트 매핑으로 로컬에서 React 개발 서버 접속 가능
ports:
- "3000:3000" # 호스트의 3000번 포트를 컨테이너의 3000번 포트와 연결 (React 개발 서버용)
# 로컬 코드 변경 사항을 컨테이너에 자동 반영
# volumes 설정 덕분에 이미지 빌드를 하지 않고 코드를 수정하면 바로 반영됩니다.
# 단, 서버는 재실행해야 반영됩니다.
volumes:
# 컨테이너 내부의 node_modules 폴더는 유지하여, 로컬 파일과 충돌 방지
- /usr/src/app/node_modules
# 현재 디렉터리의 모든 파일을 컨테이너의 /usr/src/app에 마운트 (소스 코드 실시간 반영)
- .:/usr/src/app
# 컨테이너를 실행할 때 표준 입력을 열어 인터랙티브 모드 활성화
# react 서버 구성시 사용
stdin_open: true
FROM node:alpine
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
COPY . .
CMD ["npm", "start"]
📌Docker image 빌드
✔ docker build 명령을 이용하여 image를 생성한다.
# react-app 이름으로 이미지를 빌드한다.
docker build -t react-app .
📌Docker에서 react app 실행
✔docker run 명령을 이용하여 react app를 실행한다.
docker run -it -p 3000:3000 -v /usr/src/app/node_modules -v %cd%:/usr/src/app react-app
👉 명령에 대한 설명입니다.
docker run
# 인터랙티브 모드(-i)와 터미널(-t)을 활성화하여 컨테이너 내에서 명령어 입력 가능하게 함
-it
# 호스트의 3000번 포트를 컨테이너의 3000번 포트에 연결
-p 3000:3000
# 컨테이너의 node_modules 디렉터리를 볼륨으로 유지하여 로컬파일을 참조하지 않도록 함
# 로컬 파일과 충돌 방지
-v /usr/src/app/node_modules
# 현재 디렉터리(%cd%)를 컨테이너의 /usr/src/app에 마운트하여 실시간 코드 변경 반영
-v %cd%:/usr/src/app
# 실행할 도커 이미지 (React 앱을 포함한 이미지)
react-app
💡React 앱을 개발하려면 Node.js가 필요합니다. Node.js에는 JavaScript 런타임과 함께 패키지 관리자(npm)가 포함되어 있습니다. ✔ 다운로드:Node.js 공식 홈페이지에서 LTS(Long Term Support) 버전을 다운로드하여 설치합니다. ✔ 설치 확인: 터미널(또는 명령 프롬프트)에서 아래 명령어를 입력하여 설치가 제대로 되었는지 확인합니다.
node -v
npm -v
📌 Create React App 설치
💡 Create React App은 React 프로젝트를 쉽게 생성하고 기본적인 빌드 설정을 자동으로 구성해주는 도구입니다.
express-redis-app/
│── docker-compose.yml # Docker Compose 설정 파일
│── app/ # Node.js Express 서버 코드
│ ├── Dockerfile # dockerfile
│ ├── server.js # Express 서버 코드
│ ├── package.json # Node.js 패키지 설정 파일
📌 Express 서버 구성
📝 package.json 파일 생성
✔ /app 폴더에서 아래의 명령어를 이용해서 package.json 파일을 생성한다.
npm init -y
✔ 생성된 파일 내용을 아래와 같이 변경한다.
{
"name": "express-redis-app", // 프로젝트 이름
"version": "1.0.0", // 프로젝트 버전
"main": "index.js", // 기본 실행 파일 (사용되지 않음, "start" 스크립트에서 server.js 사용)
"scripts": {
"start": "node server.js", // 서버 실행 명령어 (server.js 파일을 실행)
"test": "echo \"Error: no test specified\" && exit 1" // 테스트 스크립트 (현재 테스트 없음)
},
"keywords": [], // 프로젝트 관련 키워드 (비어 있음)
"author": "", // 작성자 정보 (비어 있음)
"license": "ISC", // 오픈소스 라이선스 (ISC 라이선스 적용)
"description": "", // 프로젝트 설명 (비어 있음)
"dependencies": {
"express": "^4.21.2", // Express.js 최신 버전 (4.21.2 이상)
"redis": "^4.7.0" // Redis 클라이언트 최신 버전 (4.7.0 이상)
}
}
🙌 local에서 구현하는 방법은 상단에 Dockerfile를 이용하여 nodejs & express 서버 설치 및 개발 환경 구성하기 링크를 이용하여 확인하세요
📝 server.js 파일 생성
✔ /app 폴더에 editor(vscode등)을 이용하여 Redis 연동코드를 server.js파일에 작성한다.
const express = require('express'); // Express.js 프레임워크 모듈 불러오기
const redis = require('redis'); // Redis 클라이언트 모듈 불러오기
const app = express();
const PORT = 3000; // 서버가 실행될 포트 번호
// Redis 클라이언트 설정
const redisClient = redis.createClient({
socket: {
host: 'redis', // Docker Compose에서 정의한 Redis 컨테이너의 이름
port: 6379 // Redis 기본 포트
}
});
// Redis 서버에 연결
redisClient.connect()
.then(() => console.log('Redis connected')) // 연결 성공 시 메시지 출력
.catch((err) => console.error("❌ Redis 연결 실패:", err)); // 연결 실패 시 오류 출력
// 기본 라우트 설정
app.get('/', async (req, res) => {
let count = await redisClient.get('count'); // Redis에서 'count' 키의 값을 가져옴
if (!count) {
count = 0; // 값이 없으면 기본값 0으로 설정
}
count = parseInt(count) + 1; // 방문자 수 증가
await redisClient.set('count', count); // 증가된 값을 Redis에 저장
res.send(`Hello World! ${count}`); // 응답으로 방문 횟수 출력
});
// 서버 실행
app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`); // 서버 실행 메시지 출력
});
📌 Express 서버용 Dockerfile 만들기
📝 /app 폴더에 Dockerfile를 생성한다.
# Node.js 18 버전의 공식 이미지를 기반으로 함
FROM node:18
# 작업 디렉터리 설정
WORKDIR /usr/src/app
# package.json과 package-lock.json을 복사
COPY package*.json .
# 의존성 설치
RUN npm install
# 모든 파일을 컨테이너로 복사
COPY . .
# 애플리케이션 실행
CMD ["npm", "start"]