Firo 설정 가이드
기본 설정
application.properties 기본 설정
properties
# Firo 활성화 설정 (기본값: true)
firo.enabled=true
# 데이터베이스 구현체 선택 (기본값: jpa)
firo.db.type=jpa # jpa 또는 mybatis
# API 라우팅 설정
firo.api-prefix=/api/firo
# 파일 업로드 기본 설정
firo.cdn-url=https://cdn.example.com/
firo.directory.base-dir=./uploads/
firo.directory.tmp-dir=./uploads/temp/
firo.directory.separator=/
firo.direct-upload-path=direct
# 최대 파일 크기 설정
spring.servlet.multipart.max-file-size=100MB
spring.servlet.multipart.max-request-size=100MB
# 프로파일 설정
spring.profiles.active=local
application.yml 기본 설정
yaml
# Firo 기본 설정
firo:
enabled: true
db:
type: jpa # jpa 또는 mybatis
cdn-url: https://cdn.example.com/
directory:
base-dir: /var/uploads/
tmp-dir: /var/uploads/temp/
separator: /
direct-upload-path: direct
# Spring Boot 파일 업로드 설정
spring:
servlet:
multipart:
max-file-size: 100MB
max-request-size: 100MB
⚠️ 조건부 Bean 로딩:
firo.db.type
설정에 따라 JPA 또는 MyBatis 구현체만 로드됩니다- Azure/S3 어댑터는 해당 설정이 있을 때만 활성화됩니다
- 필요하지 않은 어댑터 설정은 생략할 수 있습니다
데이터베이스 설정
테이블 프리픽스 설정 (선택사항)
Firo는 테이블명 앞에 프리픽스를 추가할 수 있습니다. 기본적으로는 attach
테이블을 사용하지만, 프리픽스를 설정하면 {prefix}attach
형태로 테이블명이 변경됩니다.
properties
# 테이블 프리픽스 설정 (선택사항)
firo.database.table-prefix=nv_ # nv_attach 테이블 사용
# firo.database.table-prefix=ha_ # ha_attach 테이블 사용
# firo.database.table-prefix= # attach 테이블 사용 (기본값)
사용 예시:
- 프리픽스 없음 (기본):
attach
테이블 firo.database.table-prefix=nv_
:nv_attach
테이블firo.database.table-prefix=company_
:company_attach
테이블
JPA 설정
Properties 방식
properties
firo.db.type=jpa
# JPA/Hibernate 설정
spring.jpa.hibernate.ddl-auto=validate
spring.jpa.show-sql=false
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.use_sql_comments=true
# 데이터베이스 연결
spring.datasource.url=jdbc:mysql://localhost:3306/firo_db
spring.datasource.username=firo_user
spring.datasource.password=firo_password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
YAML 방식
yaml
firo:
db:
type: jpa
spring:
jpa:
hibernate:
ddl-auto: validate
show-sql: false
properties:
hibernate:
format_sql: true
use_sql_comments: true
datasource:
url: jdbc:mysql://localhost:3306/firo_db
username: firo_user
password: firo_password
driver-class-name: com.mysql.cj.jdbc.Driver
MyBatis 설정
중요: Firo MyBatis 모듈은 외부 프로젝트에서 제공하는 SqlSessionFactory를 사용합니다. 별도의 SqlSessionFactory를 생성하지 않으므로 기존 MyBatis 설정과 충돌하지 않습니다.
기본 설정
Properties 방식
properties
# Firo MyBatis 모듈 활성화
firo.db.type=mybatis
# 기존 MyBatis 설정은 그대로 유지
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.configuration.use-generated-keys=true
# 데이터베이스 연결
spring.datasource.url=jdbc:mysql://localhost:3306/your_db
spring.datasource.username=your_user
spring.datasource.password=your_password
YAML 방식
yaml
firo:
db:
type: mybatis
# 기존 MyBatis 설정은 그대로 유지
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
use-generated-keys: true
spring:
datasource:
url: jdbc:mysql://localhost:3306/your_db
username: your_user
password: your_password
외부 프로젝트와 통합 시 필수 설정
Firo Mapper들을 외부 SqlSessionFactory에 등록하기 위해 다음과 같이 설정해야 합니다:
java
@Configuration
@MapperScans({
@MapperScan(
basePackages = "com.yourproject.mapper",
annotationClass = DefaultMapper.class, // 또는 기존 마커 어노테이션
sqlSessionFactoryRef = "defaultSqlSessionFactory"
),
@MapperScan(
basePackages = "com.unvus.firo.mybatis.mapper", // Firo Mapper 패키지
annotationClass = Mapper.class,
sqlSessionFactoryRef = "defaultSqlSessionFactory" // 동일한 SqlSessionFactory 사용
)
})
public class MyBatisConfiguration {
@Primary
@Bean("defaultSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(
@Qualifier("defaultDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// Mapper XML 위치 설정
Resource[] mapperResources = new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/*.xml");
sessionFactory.setMapperLocations(mapperResources);
// MyBatis Configuration
org.apache.ibatis.session.Configuration configuration = new org.apache.ibatis.session.Configuration();
configuration.setMapUnderscoreToCamelCase(true);
configuration.setUseGeneratedKeys(true);
// Firo Type Alias 등록 (선택사항)
configuration.getTypeAliasRegistry().registerAlias("FiroFile", com.unvus.firo.core.module.service.domain.FiroFile.class);
configuration.getTypeAliasRegistry().registerAlias("FiroAttachBag", com.unvus.firo.core.module.service.domain.AttachBag.class);
sessionFactory.setConfiguration(configuration);
return sessionFactory.getObject();
}
}
⚠️ Type Alias 관리: Firo는 외부 SqlSessionFactory를 사용하믰로 Type Alias는 사용자가 직접 등록해야 합니다. 위 예시에서처럼 configuration.getTypeAliasRegistry().registerAlias()
를 사용하거나, 패키지 스캔을 이용할 수 있습니다.
가능한 Type Alias 목록:
FiroFile
→com.unvus.firo.core.module.service.domain.FiroFile
FiroDomain
→com.unvus.firo.core.module.service.domain.FiroDomain
FiroCategory
→com.unvus.firo.core.module.service.domain.FiroCategory
AttachBag
→com.unvus.firo.core.module.service.domain.AttachBag
AttachContainer
→com.unvus.firo.core.module.service.domain.AttachContainer
SecureAccessFunc
→com.unvus.firo.core.module.service.domain.SecureAccessFunc
패키지 스캔 방식 (추천):
java
// Configuration에서 패키지 스캔으로 등록
configuration.getTypeAliasRegistry().registerAliases("com.unvus.firo.core.module.service.domain");
저장소 어댑터 설정
1. Local 어댑터 (기본값)
Properties 방식
properties
# Local 파일 시스템 사용
firo.adapters.local.directory.base-dir=/var/uploads/
firo.adapters.local.directory.tmp-dir=/var/uploads/temp/
firo.adapters.local.directory.separator=/
YAML 방식
yaml
firo:
adapters:
local:
directory:
base-dir: /var/uploads/
tmp-dir: /var/uploads/temp/
separator: /
2. Amazon S3 어댑터
Properties 방식
properties
# S3 설정
firo.s3.access-key=AKIA...
firo.s3.secret-key=your-secret-key
firo.s3.bucket=your-bucket-name
firo.s3.region=us-west-2
# CDN 설정 (선택사항)
firo.s3.cdn-url=https://d1234567890.cloudfront.net/
YAML 방식
yaml
firo:
s3:
access-key: AKIA...
secret-key: your-secret-key
bucket: your-bucket-name
region: us-west-2
cdn-url: https://d1234567890.cloudfront.net/
⚠️ 중요사항:
access-key
속성이 설정되어야 S3 Adapter가 활성화됩니다.- 설정이 누락되면 firo는 S3 없이 정상적으로 초기화됩니다.
S3 IAM 정책 예시
json
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"s3:GetObject",
"s3:PutObject",
"s3:DeleteObject",
"s3:ListBucket"
],
"Resource": [
"arn:aws:s3:::your-bucket-name",
"arn:aws:s3:::your-bucket-name/*"
]
}
]
}
3. Azure Blob Storage 어댑터
Properties 방식
properties
# Azure Blob Storage 설정 (모든 속성이 필수)
firo.azure.connection-string=DefaultEndpointsProtocol=https;AccountName=yourstorageaccount;AccountKey=your-key;EndpointSuffix=core.windows.net
firo.azure.container=files
firo.azure.cdn-url=https://yourstorageaccount.blob.core.windows.net/
YAML 방식
yaml
firo:
azure:
connection-string: >
DefaultEndpointsProtocol=https;
AccountName=yourstorageaccount;
AccountKey=your-key;
EndpointSuffix=core.windows.net
container: files
cdn-url: https://yourstorageaccount.blob.core.windows.net/
⚠️ 중요사항:
connection-string
과container
속성이 모두 설정되어야 Azure Adapter가 활성화됩니다.- 설정이 누락되면 firo는 Azure 없이 정상적으로 초기화됩니다.
- 잘못된 connection-string을 제공하면 명확한 오류 메시지가 표시됩니다.
4. FTP 어댑터
Properties 방식
properties
# FTP 서버 설정
firo.adapters.ftp.host=ftp.example.com
firo.adapters.ftp.port=21
firo.adapters.ftp.username=ftpuser
firo.adapters.ftp.password=ftppassword
firo.adapters.ftp.passive-mode=true
firo.adapters.ftp.directory.base-dir=/uploads/
firo.adapters.ftp.directory.tmp-dir=/uploads/temp/
firo.adapters.ftp.directory.separator=/
YAML 방식
yaml
firo:
adapters:
ftp:
host: ftp.example.com
port: 21
username: ftpuser
password: ftppassword
passive-mode: true
directory:
base-dir: /uploads/
tmp-dir: /uploads/temp/
separator: /
5. SFTP 어댑터
Properties 방식
properties
# SFTP 서버 설정
firo.adapters.sftp.host=sftp.example.com
firo.adapters.sftp.port=22
firo.adapters.sftp.username=sftpuser
firo.adapters.sftp.password=sftppassword
firo.adapters.sftp.private-key=/path/to/private/key
firo.adapters.sftp.known-hosts=/path/to/known_hosts
firo.adapters.sftp.directory.base-dir=/uploads/
firo.adapters.sftp.directory.tmp-dir=/uploads/temp/
YAML 방식
yaml
firo:
adapters:
sftp:
host: sftp.example.com
port: 22
username: sftpuser
password: sftppassword
private-key: /path/to/private/key
known-hosts: /path/to/known_hosts
directory:
base-dir: /uploads/
tmp-dir: /uploads/temp/
이미지 필터 설정
전역 필터 설정
properties
# EXIF 정보를 읽어 이미지 자동 회전
firo.filters.auto-fix-orientation.enabled=true
# 이미지 리사이징 설정
firo.filters.resize.enabled=true
firo.filters.resize.max-width=1920
firo.filters.resize.max-height=1080
firo.filters.resize.quality=0.8
# 최대 파일 크기 제한 (바이트)
firo.filters.max-size.enabled=true
firo.filters.max-size.image-max-size=10485760 # 10MB
firo.filters.max-size.document-max-size=52428800 # 50MB
# 확장자 제한
firo.filters.extension.enabled=true
firo.filters.extension.allowed-extensions=jpg,jpeg,png,gif,pdf,doc,docx,xls,xlsx
# 고정 차원 검증
firo.filters.fixed-dimension.enabled=false
firo.filters.fixed-dimension.width=200
firo.filters.fixed-dimension.height=200
Java Configuration으로 필터 설정
java
@Configuration
public class FiroFilterConfiguration {
@Bean
public FiroFilterRegistry firoFilterRegistry() {
FiroFilterRegistry registry = new FiroFilterRegistry();
// 이미지 필터 체인
FiroFilterChain imageFilterChain = FiroFilterChain.builder()
.add(new FileSizeExceptionFilter(10 * 1024 * 1024)) // 10MB
.add(new FileExtensionExceptionFilter(Set.of("jpg", "jpeg", "png", "gif")))
.add(new AutoFixOrientationImageFilter())
.add(new ResizeImageFilter(1920, 1080))
.build();
// 문서 필터 체인
FiroFilterChain documentFilterChain = FiroFilterChain.builder()
.add(new FileSizeExceptionFilter(50 * 1024 * 1024)) // 50MB
.add(new FileExtensionExceptionFilter(Set.of("pdf", "doc", "docx", "xls", "xlsx")))
.build();
registry.register("image", imageFilterChain);
registry.register("document", documentFilterChain);
return registry;
}
}
도메인/카테고리별 설정
Java Configuration 예시
java
@Configuration
public class FiroConfigurationExample {
@PostConstruct
public void configureFiroDomains() {
// 사용자 프로필 이미지 도메인
FiroDomain userProfileDomain = FiroDomain.builder("user-profile")
.adapter(FiroRegistry.getAdapter(AdapterType.S3))
.directoryPathPolicy(new DateDirectoryPathPolicy(
DateDirectoryPathPolicy.DATE_SUBDIR_TYPE.YYYY_MM,
"/user-profiles/",
"/temp/"
))
.build();
// 프로필 이미지 카테고리 (리사이즈 필터 적용)
FiroCategory avatarCategory = FiroCategory.builder(userProfileDomain, "avatar")
.filterChain(FiroFilterChain.builder()
.add(new AutoFixOrientationImageFilter())
.add(new ResizeImageFilter(200, 200))
.build())
.keepExt(true)
.build();
userProfileDomain.addCategory(avatarCategory);
FiroRegistry.add(userProfileDomain);
// 게시글 첨부파일 도메인
FiroDomain postAttachmentDomain = FiroDomain.builder("post-attachment")
.adapter(FiroRegistry.getAdapter(AdapterType.LOCAL))
.build();
FiroCategory documentCategory = FiroCategory.builder(postAttachmentDomain, "document")
.filterChain(FiroFilterChain.builder()
.add(new MaxSizeExceptionFilter(50 * 1024 * 1024)) // 50MB
.build())
.build();
postAttachmentDomain.addCategory(documentCategory);
FiroRegistry.add(postAttachmentDomain);
}
}
환경별 설정
개발 환경 (application-dev.properties)
Properties 방식
properties
# Firo 기본 설정
firo.enabled=true
firo.db.type=jpa
# 개발 환경에서는 Local 어댑터 사용
firo.directory.base-dir=./uploads/
firo.directory.tmp-dir=./uploads/temp/
firo.api-prefix=/api/firo
# 디버깅을 위한 로깅
logging.level.com.unvus.firo=DEBUG
logging.level.org.springframework.web.multipart=DEBUG
YAML 방식 (application-dev.yml)
yaml
firo:
enabled: true
db:
type: jpa
directory:
base-dir: ./uploads/
tmp-dir: ./uploads/temp/
logging:
level:
com.unvus.firo: DEBUG
org.springframework.web.multipart: DEBUG
테스트 환경 (application-test.properties)
properties
# Firo 테스트 설정
firo.enabled=true
firo.db.type=jpa
# 테스트용 H2 인메모리 DB
spring.datasource.url=jdbc:h2:mem:testdb
spring.jpa.hibernate.ddl-auto=create-drop
# 테스트용 임시 디렉토리
firo.directory.base-dir=/tmp/test-uploads/
firo.directory.tmp-dir=/tmp/test-uploads/temp/
프로덕션 환경 (application-prod.properties)
properties
# Firo 프로덕션 설정
firo.enabled=true
firo.db.type=mybatis
# 프로덕션에서는 S3 사용
firo.s3.access-key=${AWS_ACCESS_KEY_ID}
firo.s3.secret-key=${AWS_SECRET_ACCESS_KEY}
firo.s3.bucket=${S3_BUCKET_NAME}
firo.s3.region=${AWS_REGION}
# 보안을 위한 로깅 레벨 조정
logging.level.com.unvus.firo=INFO
logging.level.org.springframework.web.multipart=WARN
iflex 통합 환경 (application-iflex.properties)
properties
# iflex와 함께 사용 시 권장 설정
firo.enabled=true
firo.db.type=mybatis # iflex는 MyBatis 사용
# iflex 테이블 프리픽스와 맞춤
firo.database.table-prefix=
# MyBatis Type Alias 충돌 방지 (자동 처리됨)
# MyBatis 로깅 (필요시)
logging.level.com.unvus.firo.mybatis.mapper=DEBUG
# 필터 체인 전역 설정
firo.filters.auto-fix-orientation.enabled=true
firo.filters.resize.enabled=true
firo.filters.max-size.enabled=true
보안 설정
CORS 설정 (파일 업로드 API용)
java
@Configuration
public class CorsConfiguration {
@Bean
public CorsConfigurationSource corsConfigurationSource() {
CorsConfiguration configuration = new CorsConfiguration();
configuration.setAllowedOriginPatterns(Arrays.asList("*"));
configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE", "OPTIONS"));
configuration.setAllowedHeaders(Arrays.asList("*"));
configuration.setAllowCredentials(true);
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/api/firo/**", configuration);
return source;
}
}
파일 타입 제한
java
@Component
public class FileTypeValidator {
private static final Set<String> ALLOWED_IMAGE_TYPES = Set.of(
"image/jpeg", "image/png", "image/gif", "image/webp"
);
private static final Set<String> ALLOWED_DOCUMENT_TYPES = Set.of(
"application/pdf", "text/plain",
"application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
);
public boolean isValidFileType(String contentType, String category) {
if ("image".equals(category)) {
return ALLOWED_IMAGE_TYPES.contains(contentType);
} else if ("document".equals(category)) {
return ALLOWED_DOCUMENT_TYPES.contains(contentType);
}
return false;
}
}
문제 해결
설정 중 문제가 발생하면 트러블슈팅 가이드를 참고하세요.
주요 해결 방법:
- Azure/S3 초기화 실패: connection-string이나 access-key 설정 확인
- MyBatis Type Alias 충돌: Firo는 자동으로 충돌을 방지합니다
- 파일 업로드 권한 오류: 디렉토리 권한 및 존재 여부 확인