Skip to content

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

🎯 다음 단계

  1. API 가이드 - 상세한 API 사용법
  2. 설정 가이드 - 고급 설정 방법
  3. 트러블슈팅 가이드 - 문제 해결 방법

💡 팁: 이 가이드를 따라하다가 문제가 생기면 logging.level.com.unvus.firo: DEBUG를 설정해서 로그를 확인해보세요!