Firo 퀵스타트 가이드
Firo 라이브러리 빠른 시작 가이드입니다.
🚀 5분만에 시작하기
1단계: 의존성 추가 (30초)
Gradle 프로젝트
gradle
// build.gradle
dependencies {
implementation 'com.unvus.firo:firo-core:1.3.1-SNAPSHOT'
implementation 'com.unvus.firo:firo-jpa:1.3.1-SNAPSHOT' // JPA 사용 시
// 또는
// implementation 'com.unvus.firo:firo-mybatis:1.3.1-SNAPSHOT' // MyBatis 사용 시
}
Maven 프로젝트
xml
<!-- pom.xml -->
<dependencies>
<dependency>
<groupId>com.unvus.firo</groupId>
<artifactId>firo-core</artifactId>
<version>1.3.1-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.unvus.firo</groupId>
<artifactId>firo-jpa</artifactId> <!-- JPA 사용 시 -->
<version>1.3.1-SNAPSHOT</version>
</dependency>
</dependencies>
2단계: 기본 설정 (1분)
application.yml
yaml
# 기본 설정
firo:
enabled: true
db:
type: jpa # jpa 또는 mybatis
directory:
base-dir: ./uploads/
tmp-dir: ./uploads/temp/
cdn-url: http://localhost:8080/
api-prefix: /api/firo
# 개발 환경 설정
spring:
profiles:
active: local
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
---
# 로컬 개발 환경
spring:
config:
activate:
on-profile: local
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: ""
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create-drop
show-sql: true
logging:
level:
com.unvus.firo: DEBUG
---
# 개발 서버 환경
spring:
config:
activate:
on-profile: dev
datasource:
url: jdbc:mysql://localhost:3306/firo_dev
username: firo_dev
password: password
jpa:
hibernate:
ddl-auto: update
logging:
level:
com.unvus.firo: DEBUG
---
# 운영 환경
spring:
config:
activate:
on-profile: prod
datasource:
url: jdbc:mysql://${DB_HOST}:${DB_PORT}/${DB_NAME}
username: ${DB_USERNAME}
password: ${DB_PASSWORD}
jpa:
hibernate:
ddl-auto: none
firo:
s3:
access-key: ${AWS_ACCESS_KEY_ID}
secret-key: ${AWS_SECRET_ACCESS_KEY}
bucket: ${S3_BUCKET}
region: ${AWS_REGION}
cdn-url: ${CDN_URL}
logging:
level:
com.unvus.firo: INFO
application.properties (YAML 대신 사용 시)
properties
# Firo 기본 설정
firo.enabled=true
firo.db.type=jpa
firo.directory.base-dir=./uploads/
firo.directory.tmp-dir=./uploads/temp/
firo.cdn-url=http://localhost:8080/
firo.api-prefix=/api/firo
# Spring Boot 파일 업로드 설정
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
# 프로필 설정
spring.profiles.active=local
3단계: 데이터베이스 테이블 생성 (1분)
H2 Database (개발용)
yaml
# application.yml에 추가
spring:
datasource:
url: jdbc:h2:mem:testdb
driver-class-name: org.h2.Driver
username: sa
password: ""
h2:
console:
enabled: true
jpa:
hibernate:
ddl-auto: create-drop # 개발 시에만 사용
MySQL (운영용)
sql
-- 테이블 생성 스크립트
CREATE TABLE attach (
attach_id BIGINT AUTO_INCREMENT PRIMARY KEY,
attach_ref_domain VARCHAR(50) NOT NULL,
attach_ref_key BIGINT NOT NULL,
attach_ref_category VARCHAR(50) NOT NULL DEFAULT 'default',
attach_display_name VARCHAR(255) NOT NULL,
attach_saved_name VARCHAR(255) NOT NULL,
attach_saved_dir VARCHAR(500) NOT NULL,
attach_file_type VARCHAR(100),
attach_file_size BIGINT,
attach_deleted BOOLEAN DEFAULT FALSE,
attach_ext VARCHAR(100),
attach_created_by BIGINT,
attach_created_dt TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
INDEX idx_attach_ref (attach_ref_domain, attach_ref_key, attach_ref_category),
INDEX idx_attach_deleted (attach_deleted)
);
4단계: 첫 번째 파일 업로드 코드 작성 (2분)
FiroService를 이용한 기본 업로드
java
package com.example.demo.service;
import com.unvus.firo.core.module.service.FiroService;
import com.unvus.firo.core.module.service.domain.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.time.LocalDateTime;
import java.util.List;
@Service
public class FileUploadService {
@Autowired
private FiroService firoService;
/**
* 사용자 프로필 이미지 업로드
*/
public String uploadUserProfile(Long userId, MultipartFile file) {
try {
// 1. 임시 업로드
FiroCategory category = FiroRegistry.get("user", "profile");
String tempUuid = firoService.uploadTemp(file, category, null, true);
// 2. AttachBag 생성
AttachBag bag = new AttachBag("user");
FiroFile firoFile = new FiroFile();
firoFile.setDisplayName(file.getOriginalFilename());
firoFile.setSavedName(tempUuid);
firoFile.setFileType(file.getContentType());
firoFile.setFileSize(file.getSize());
bag.add("profile", firoFile);
// 3. 실제 저장
List<FiroFile> savedFiles = firoService.save(
userId, bag, LocalDateTime.now(), false, false
);
return savedFiles.get(0).getCdnUrl(); // CDN URL 반환
} catch (Exception e) {
throw new RuntimeException("파일 업로드 실패: " + e.getMessage(), e);
}
}
/**
* 사용자 프로필 이미지 조회
*/
public List<FiroFile> getUserProfileImages(Long userId) {
return firoService.listAttachByRef("user", userId, "profile");
}
/**
* 모든 사용자 파일 조회 (AttachBag 형태)
*/
public AttachBag getAllUserFiles(Long userId) {
return firoService.getAttachBagByRef("user", userId, null);
}
}
컨트롤러 작성
java
package com.example.demo.controller;
import com.example.demo.service.FileUploadService;
import com.unvus.firo.core.module.service.domain.FiroFile;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/files")
public class FileController {
@Autowired
private FileUploadService fileUploadService;
/**
* 프로필 이미지 업로드
*/
@PostMapping("/profile/{userId}")
public ResponseEntity<?> uploadProfile(
@PathVariable Long userId,
@RequestParam("file") MultipartFile file) {
try {
String imageUrl = fileUploadService.uploadUserProfile(userId, file);
return ResponseEntity.ok(Map.of(
"success", true,
"imageUrl", imageUrl,
"message", "업로드 성공"
));
} catch (Exception e) {
return ResponseEntity.badRequest()
.body(Map.of(
"success", false,
"message", e.getMessage()
));
}
}
/**
* 사용자 프로필 이미지 목록 조회
*/
@GetMapping("/profile/{userId}")
public ResponseEntity<List<FiroFile>> getProfileImages(@PathVariable Long userId) {
List<FiroFile> images = fileUploadService.getUserProfileImages(userId);
return ResponseEntity.ok(images);
}
}
5단계: 도메인/카테고리 설정 (1분)
java
package com.example.demo.config;
import com.unvus.firo.core.module.service.FiroRegistry;
import com.unvus.firo.core.module.service.domain.FiroDomain;
import org.springframework.context.annotation.Configuration;
import javax.annotation.PostConstruct;
@Configuration
public class FiroConfiguration {
@PostConstruct
public void setupFiroDomains() {
// 사용자 관련 파일들
FiroDomain userDomain = FiroDomain.builder("user").build();
userDomain.addCategory("profile"); // 프로필 사진
userDomain.addCategory("avatar"); // 아바타
userDomain.addCategory("document"); // 문서
FiroRegistry.add(userDomain);
// 게시글 관련 파일들
FiroDomain postDomain = FiroDomain.builder("post").build();
postDomain.addCategory("image"); // 게시글 이미지
postDomain.addCategory("attachment"); // 첨부파일
FiroRegistry.add(postDomain);
System.out.println("✅ Firo 도메인 설정 완료");
}
}
🧪 테스트해보기
1. 애플리케이션 실행
bash
./gradlew bootRun
# 또는
mvn spring-boot:run
2. 파일 업로드 테스트 (curl 사용)
bash
# 프로필 이미지 업로드
curl -X POST \
http://localhost:8080/api/files/profile/1 \
-F 'file=@test-image.jpg'
# 응답 예시:
# {"success":true,"imageUrl":"http://localhost:8080/user/profile/secure-name.jpg","message":"업로드 성공"}
3. 파일 목록 조회 테스트
bash
# 사용자 1의 프로필 이미지 목록 조회
curl http://localhost:8080/api/files/profile/1
# 응답 예시:
# [{"id":1,"displayName":"test-image.jpg","cdnUrl":"http://localhost:8080/user/profile/secure-name.jpg"}]
4. Firo 내장 REST API 테스트
bash
# 임시 파일 업로드
curl -X POST \
http://localhost:8080/api/firo/attach/tmp \
-F 'file=@test.jpg' \
-F 'refDomain=user' \
-F 'refCategory=profile'
# AttachBag 조회
curl http://localhost:8080/api/firo/attach/user/1
📖 자주 사용하는 패턴들
패턴 1: 간단한 파일 업로드
java
// 한 줄로 파일 업로드
String fileUrl = firoService.uploadDirect(
file, "product", "image", productId, LocalDateTime.now(), true
);
패턴 2: 이미지 필터 적용
java
// 이미지 리사이징과 회전 보정
FiroFilterChain filterChain = FiroFilterChain.builder()
.add(new AutoFixOrientationImageFilter())
.add(new ResizeImageFilter(800, 600))
.build();
String tempUuid = firoService.uploadTemp(file, category, filterChain, true);
패턴 3: 여러 파일 동시 업로드
java
AttachBag bag = new AttachBag("product");
for (MultipartFile file : files) {
String tempUuid = firoService.uploadTemp(file, category, null, true);
FiroFile firoFile = new FiroFile();
firoFile.setDisplayName(file.getOriginalFilename());
firoFile.setSavedName(tempUuid);
firoFile.setFileType(file.getContentType());
firoFile.setFileSize(file.getSize());
bag.add("images", firoFile);
}
List<FiroFile> savedFiles = firoService.save(
productId, bag, LocalDateTime.now(), false, false
);
🔧 환경별 설정
개발 환경
yaml
firo:
enabled: true
db:
type: jpa
directory:
base-dir: ./dev-uploads/
tmp-dir: ./dev-uploads/temp/
cdn-url: http://localhost:8080/
logging:
level:
com.unvus.firo: DEBUG
운영 환경 (S3 사용)
yaml
firo:
enabled: true
db:
type: mybatis
s3:
access-key: ${AWS_ACCESS_KEY_ID}
secret-key: ${AWS_SECRET_ACCESS_KEY}
bucket: ${S3_BUCKET_NAME}
region: ${AWS_REGION}
cdn-url: ${CLOUDFRONT_URL}
logging:
level:
com.unvus.firo: INFO
❓ 자주 묻는 질문
Q1: 파일이 업로드되지 않아요!
bash
# 디렉토리 권한 확인
ls -la uploads/
chmod 755 uploads/
# 로그 확인
tail -f logs/spring.log
Q2: CDN URL이 제대로 생성되지 않아요!
yaml
# application.yml 확인
firo:
cdn-url: http://localhost:8080/ # 반드시 /로 끝나야 함
Q3: 데이터베이스 연결 오류가 나요!
yaml
# H2 사용 시 (개발)
spring:
datasource:
url: jdbc:h2:mem:testdb
jpa:
hibernate:
ddl-auto: create-drop
# MySQL 사용 시 (운영)
spring:
datasource:
url: jdbc:mysql://localhost:3306/your_database
username: your_username
password: your_password
🎯 다음 단계
💡 팁: 이 가이드를 따라하다가 문제가 생기면 logging.level.com.unvus.firo: DEBUG
를 설정해서 로그를 확인해보세요!