Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
3aed4f0
Release user-service-v0.0.1-beta-SNAPSHOT
can019 Sep 19, 2025
ee39850
Spring Quartz, 스케줄링 작업(Workflow 및 Job, Task 세팅) (#131)
jihukimme Sep 19, 2025
0bed21b
Revert "Spring Quartz, 스케줄링 작업(Workflow 및 Job, Task 세팅) (#131)" (#138)
jihukimme Sep 19, 2025
5316a53
onnx 모델 수동 주입 및 logging_config.py 파일 원래대로 변경
rll2641 Sep 19, 2025
1eafe92
핫픽스 경로 수정
rll2641 Sep 19, 2025
c339364
ci/cd 코드 수정
rll2641 Sep 19, 2025
1981f39
env 변수 추가
rll2641 Sep 19, 2025
c617176
Release user-service-v0.0.1-beta-SNAPSHOT-12
can019 Sep 19, 2025
32688a6
pre-processing-v0.0.1-beta-SNAPSHOT-9
rll2641 Sep 20, 2025
9d3ca6d
Release user-service-v0.0.1-beta-SNAPSHOT
can019 Sep 22, 2025
8cc1e26
fix: openapi3 task ci에 추가
can019 Sep 22, 2025
decdb4a
fix: Swagger ui generate 시 token 추가
can019 Sep 22, 2025
b87e404
chore: Swagger deploy auth github token에서 pat으로 전환
can019 Sep 22, 2025
82b6dd3
pre-processing-v0.0.1-beta-SNAPSHOT-10
rll2641 Sep 22, 2025
c2f89c6
pre-processing-v0.0.1-beta-SNAPSHOT-11
rll2641 Sep 22, 2025
3a7ad77
Release user-service-v0.0.1-beta-SNAPSHOT
can019 Sep 22, 2025
714afbd
fix: application-produciton.yml mail 설정 들여쓰기
can019 Sep 22, 2025
ea8b594
pre-processing-v0.0.1-beta-SNAPSHOT-12
rll2641 Sep 23, 2025
91a90de
clean up build.gradle (#182)
can019 Sep 23, 2025
8e70aa9
User register api docs (test) 추가 (#183)
can019 Sep 23, 2025
a9003a1
common/utils javadoc 추가 (#184)
can019 Sep 23, 2025
fd3bacd
Merge pull request #187 from Kernel180-BE12/develop
thkim7 Sep 23, 2025
d833f58
Merge branch 'develop' into release/user-service-v0.0.1-beta-stable
can019 Sep 23, 2025
0fcb00b
Merge pull request #189 from Kernel180-BE12/develop
thkim7 Sep 23, 2025
8721117
doc: javadoc 작성
bwnfo3 Sep 23, 2025
a56eb78
chore: spotlessApply
bwnfo3 Sep 23, 2025
15e3397
Common/utils javadoc fix H3 to H2 (#191)
can019 Sep 23, 2025
fda507f
Merge pull request #193 from Kernel180-BE12/release/javadoc-dy
bwnfo3 Sep 23, 2025
3dd19fe
Merge pull request #194 from Kernel180-BE12/develop
thkim7 Sep 23, 2025
fc89190
Ci에 javadoc 추가 및 javadoc task 실패 fix (#195)
can019 Sep 23, 2025
7b9d61a
Merge branch 'main' into release/user-service-v0.0.1-beta-stable
can019 Sep 23, 2025
207dd66
Workflow 관련 javadoc 작성 (#196)
jihukimme Sep 23, 2025
e308781
fix: 임시로 test disable
can019 Sep 23, 2025
465398a
chore: Test disable 원복
can019 Sep 23, 2025
db1eff6
fix: docker 타입아웃 증가 (#197)
kakusiA Sep 23, 2025
be20512
feat: blog_create_log 원인 파악을 위한 로그
thkim7 Sep 24, 2025
55b734b
Merge pull request #198 from Kernel180-BE12/feature/blog_create_log
thkim7 Sep 24, 2025
827c84a
fix: 환경변수 변경및 log 버그 수정 (#199)
kakusiA Sep 24, 2025
16318b5
fix : docker file 및 docker compose 수정
rll2641 Sep 24, 2025
749a6c1
fix : trace_id MDC에 추가
rll2641 Sep 24, 2025
bcd685d
pre-processing-v0.0.1-beta-SNAPSHOT-22
rll2641 Sep 25, 2025
fc94d0d
hofix : 도커 파일 수정 (리눅스 업데이트)
rll2641 Sep 25, 2025
41d8193
hofix : 도커 컴포즈 수정 (크롬에 필요한 외부 라이브러리들 컨테이너 내부 설치 -> 외부 주입 변경)
rll2641 Sep 25, 2025
26cc011
hofix : EC2 환경 차이 및 알수 없는 오류로 롤백
rll2641 Sep 25, 2025
673f819
E2e 테스트 cookie interceptor 추가 및 적용 (#202)
can019 Sep 25, 2025
acd1adf
Release user-service-v0.0.1-beta-stable (#181)
can019 Sep 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .github/workflows/ci-java.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,10 @@ jobs:
run: |
./gradlew unitTest
./gradlew integrationTest
./gradlew javadoc
if [ "${{ github.base_ref }}" = "main" ] || [[ "${{ github.ref }}" == refs/tags/* ]]; then
./gradlew e2eTest
./gradlew openapi3
fi
working-directory: apps/user-service

Expand Down Expand Up @@ -161,6 +163,12 @@ jobs:
- set-image-tag
if: startsWith(github.ref, 'refs/tags/user-service-v')

permissions:
contents: read # 리포지토리 읽기
pages: write # GitHub Pages 쓰기
id-token: write # OIDC 토큰
actions: read # Actions 읽기

steps:
- name: Checkout repository
uses: actions/checkout@v4
Expand Down Expand Up @@ -192,12 +200,13 @@ jobs:
with:
output: user-service-swagger-ui-${{ needs.set-image-tag.outputs.image-tag }}
spec-file: openapi-spec/openapi3.yaml
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

- name: Deploy to GitHub Pages
if: steps.check-openapi.outputs.openapi_exists == 'true'
uses: peaceiris/actions-gh-pages@v3
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
personal_token: ${{ secrets.PERSONAL_TOKEN }}
publish_dir: ./user-service-swagger-ui-${{ needs.set-image-tag.outputs.image-tag }}
destination_dir: user-service/${{ needs.set-image-tag.outputs.image-tag }}

Expand Down
2 changes: 1 addition & 1 deletion apps/pre-processing-service/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -64,4 +64,4 @@ COPY . .
ENV MECAB_PATH=/usr/lib/mecab/dic/ipadic

# (권장 대안) 코드에서 uvicorn import 안 하고 프로세스 매니저로 실행하려면:
ENTRYPOINT ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "app.main:app", "-b", "0.0.0.0:8000"]
ENTRYPOINT ["gunicorn", "-k", "uvicorn.workers.UvicornWorker", "app.main:app", "-b", "0.0.0.0:8000", "--timeout", "120"]
2 changes: 1 addition & 1 deletion apps/pre-processing-service/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ class BaseSettingsConfig(BaseSettings):
MAX_IMAGE_SIZE_MB: int = 10

# 테스트/추가용 필드
openai_api_key: Optional[str] = None # << 이 부분 추가
OPENAI_API_KEY: Optional[str] = None # << 이 부분 추가

def __init__(self, **kwargs):
super().__init__(**kwargs)
Expand Down
29 changes: 15 additions & 14 deletions apps/pre-processing-service/app/service/blog/blog_create_service.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,27 @@
import json
import logging
import os
from loguru import logger
from datetime import datetime
from typing import Dict, List, Optional, Any

from openai import OpenAI
from dotenv import load_dotenv

from app.core.config import settings
from app.model.schemas import RequestBlogCreate
from app.errors.BlogPostingException import *

# 환경변수 로드
load_dotenv(".env.dev")


class BlogContentService:
"""RAG를 사용한 블로그 콘텐츠 생성 전용 서비스"""

def __init__(self):
# OpenAI API 키 설정
self.openai_api_key = os.getenv("OPENAI_API_KEY")
self.openai_api_key = settings.OPENAI_API_KEY
if not self.openai_api_key:
raise ValueError("OPENAI_API_KEY가 .env.dev 파일에 설정되지 않았습니다.")

# 인스턴스 레벨에서 클라이언트 생성
self.client = OpenAI(api_key=self.openai_api_key)
logging.basicConfig(level=logging.INFO)
self.logger = logging.getLogger(__name__)

def generate_blog_content(self, request: RequestBlogCreate) -> Dict[str, Any]:
"""
Expand All @@ -39,20 +34,26 @@ def generate_blog_content(self, request: RequestBlogCreate) -> Dict[str, Any]:
Dict: {"title": str, "content": str, "tags": List[str]} 형태의 결과
"""
try:
# 1. 콘텐츠 정보 정리
logger.debug("[STEP1] 콘텐츠 컨텍스트 준비 시작")
content_context = self._prepare_content_context(request)
logger.debug(f"[STEP1 완료] context length={len(content_context)}")

# 2. 프롬프트 생성
logger.debug("[STEP2] 프롬프트 생성 시작")
prompt = self._create_content_prompt(content_context, request)
logger.debug(f"[STEP2 완료] prompt length={len(prompt)}")

# 3. GPT를 통한 콘텐츠 생성
logger.debug("[STEP3] OpenAI API 호출 시작")
generated_content = self._generate_with_openai(prompt)
logger.debug(f"[STEP3 완료] generated length={len(generated_content)}")

logger.debug("[STEP4] 콘텐츠 파싱 시작")
result = self._parse_generated_content(generated_content, request)
logger.debug("[STEP4 완료]")

# 4. 콘텐츠 파싱 및 구조화
return self._parse_generated_content(generated_content, request)
return result

except Exception as e:
self.logger.error(f"콘텐츠 생성 실패: {e}")
logger.error(f"콘텐츠 생성 실패: {e}")
return self._create_fallback_content(request)

def _prepare_content_context(self, request: RequestBlogCreate) -> str:
Expand Down
5 changes: 2 additions & 3 deletions apps/pre-processing-service/app/utils/llm_extractor.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import os
from openai import OpenAI
from dotenv import load_dotenv

load_dotenv()
from app.core.config import settings


class LLMExtractor:
Expand All @@ -13,7 +12,7 @@ def __init__(self, model="gpt-4o"):
:param model: 사용할 LLM 모델 이름
"""

self.client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
self.client = OpenAI(api_key=settings.OPENAI_API_KEY)
self.model = model

def login_extraction_prompt(self, target_description: str, html: str):
Expand Down
9 changes: 3 additions & 6 deletions apps/user-service/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
}

group = 'site.icebang'
version = '0.0.1-alpha-SNAPSHOT'
version = '0.0.1-beta-STABLE'
description = 'Ice bang - fast campus team4'

java {
Expand All @@ -21,7 +21,6 @@ configurations {
compileOnly {
extendsFrom annotationProcessor
}
// Spring Boot의 기본 로깅(Logback) 제외
all {
exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
}
Expand Down Expand Up @@ -50,18 +49,16 @@ dependencies {
// Retry
implementation 'org.springframework.retry:spring-retry'

// logging
implementation 'org.springframework.boot:spring-boot-starter-log4j2'
implementation 'com.fasterxml.jackson.dataformat:jackson-dataformat-yaml'
implementation 'org.apache.logging.log4j:log4j-layout-template-json'
implementation 'pl.tkowalcz.tjahzi:log4j2-appender-nodep:0.9.17'
implementation 'org.apache.httpcomponents:httpclient:4.5.14'
implementation 'org.apache.httpcomponents:httpcore:4.4.16'

// 비동기 로깅
// implementation 'com.lmax:disruptor:3.4.4'
// implementation 'org.apache.commons:commons-dbcp2'
// implementation 'org.apache.commons:commons-pool2'

// micrometer & actuator
implementation "io.micrometer:micrometer-tracing-bridge-brave"
implementation "io.micrometer:micrometer-tracing"
implementation 'io.micrometer:micrometer-registry-prometheus'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,34 +4,123 @@

import lombok.Data;

/**
* 공통 APi 응답 DTO 클래스입니다.
*
* <p>REST API의 응답 형식을 표준화하기 위해 사용됩니다. 모든 응답은 성공 여부({@link #success}), 응답 데이터({@link #data}), 응답
* 메시지({@link #message}), 그리고 HTTP 상태 코드({@link #status})를 포함합니다.
*
* <p><b>사용 예시:</b>
*
* <pre>{@code
* // 성공 응답 생성
* ApiResponse<UserDto> response = ApiResponse.success(userDto);
*
* // 메시지를 포함한 성공 응답
* ApiResponse<UserDto> response = ApiResponse.success(userDto, "회원 조회 성공");
*
* // 오류 응답 생성
* ApiResponse<Void> errorResponse = ApiResponse.error("잘못된 요청입니다.", HttpStatus.BAD_REQUEST);
* }</pre>
*
* @param <T> 응답 데이터의 타입
* @author jys01012@gmail.com
* @since v0.0.1-alpha
* @see HttpStatus
* @see lombok.Data
*/
@Data
public class ApiResponse<T> {
/**
* 요청 처리 성공 여부.
*
* <p>true: 요청이 정상적으로 처리됨 false: 요청 처리 중 오류 발생
*/
private boolean success;

/**
* 실제 응답 데이터(payload).
*
* <p>요청이 성공적으로 처리되었을 경우 반환되는 데이터이며, 실패 시에는 {@code null}일 수 있습니다.
*/
private T data;

/**
* 응답 메세지.
*
* <p>성공 또는 오류 상황을 설명하는 메시지를 담습니다. 클라이언트에서 사용자에게 직접 표시할 수도 있습니다.
*/
private String message;

/**
* HTTP 상태 코드.
*
* <p>Spring의 {@link HttpStatus} 열거형을 사용합니다.
*/
private HttpStatus status; // HttpStatus로 변경

/** 기본 생성자입니다. 모든 필드가 기본값으로 초기화됩니다. */
public ApiResponse() {}

/**
* 모든 필드를 초기화하는 생성자.
*
* @param success 요청 성공 여부
* @param data 응답 데이터
* @param message 응답 메시지
* @param status HTTP 상태 코드
*/
public ApiResponse(boolean success, T data, String message, HttpStatus status) {
this.success = success;
this.data = data;
this.message = message;
this.status = status;
}

/**
* 성공 응답을 생성합니다. (기본 메시지: "OK", 상태: 200 OK)
*
* @param data 응답 데이터
* @param <T> 데이터 타입
* @return 성공 응답 객체
*/
public static <T> ApiResponse<T> success(T data) {
return new ApiResponse<>(true, data, "OK", HttpStatus.OK);
}

/**
* 성공 응답을 생성합니다. (상태: 200 OK)
*
* @param data 응답 데이터
* @param message 사용자 정의 메시지
* @param <T> 데이터 타입
* @return 성공 응답 객체
*/
public static <T> ApiResponse<T> success(T data, String message) {
return new ApiResponse<>(true, data, message, HttpStatus.OK);
}

/**
* 성공 응답을 생성합니다.
*
* @param data 응답 데이터
* @param message 사용자 정의 메시지
* @param status 사용자 정의 상태 코드
* @param <T> 데이터 타입
* @return 성공 응답 객체
*/
public static <T> ApiResponse<T> success(T data, String message, HttpStatus status) {
return new ApiResponse<>(true, data, message, status);
}

/**
* 오류 응답을 생성합니다.
*
* @param message 오류 메시지
* @param status HTTP 상태 코드
* @param <T> 데이터 타입
* @return 오류 응답 객체
*/
public static <T> ApiResponse<T> error(String message, HttpStatus status) {
return new ApiResponse<>(false, null, message, status);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,90 @@

import lombok.Data;

/**
* 페이징, 검색, 정렬, 필터링을 위한 공통 매개변수 클래스입니다.
*
* <p>목록 조회 API에서 공통적으로 사용되는 요청 파라미터를 정의합니다. 현재 페이지 번호({@link #current}), 페이지 크기({@link #pageSize}),
* 검색어({@link #search}), 정렬 조건({@link #sorters}), 필터 조건({@link #filters})를 포함합니다.
*
* <p><b>사용 예시:</b>
*
* <pre>{@code
* PageParams params = new PageParams();
* params.setCurrent(2);
* params.setPageSize(20);
* params.setSearch("회원");
*
* int offset = params.getOffset(); // 20
* boolean searchable = params.hasSearch(); // true
* }</pre>
*
* @author jys01012@gmail.com
* @since v0.0.1-alpha
* @see lombok.Data
*/
@Data
public class PageParams {
/**
* 현재 페이지 번호 (1부터 시작).
*
* <p>1부터 시작하며, 기본값은 1입니다. 0 이하의 값은 유효하지 않습니다.
*/
private int current = 1;

/**
* 한 펭지에 표시할 데이터 개수.
*
* <p>한 페이지에 표시할 항목의 개수를 지정합니다. 기본값은 10개이며, 일반적으로 10, 20, 50, 100 등의 값을 사용합니다.
*/
private int pageSize = 10;

/**
* 검색어.
*
* <p>목록에서 특정 조건으로 검색할 때 사용되는 키워드입니다. {@code null}이거나 빈 문자열인 경우 검색 조건이 적용되지 않습니다.
*/
private String search;

/**
* 정렬 조건 배열.
*
* <p>예: {@code ["name:asc", "createdAt:desc"]} API 설계에 따라 "필드명:정렬방향" 형식을 권장합니다. {@code null}이거나 빈
* 배열인 경우 기본 정렬이 적용됩니다.
*/
private String[] sorters;

/**
* 필터링 조건 배열.
*
* <p>예: {@code ["status:active", "role:admin"]} 각 요소는 특정 필드에 대한 필터링 조건을 나타냅니다. 형태는 구현에 따라 다를 수
* 있습니다.
*/
private String[] filters;

// 계산된 offset
/**
* 페이징 처리를 위한 offset(시작 위치)을 계산합니다.
*
* @return (current - 1) * pageSize
*/
public int getOffset() {
return (current - 1) * pageSize;
}

/**
* 검색어가 유효하게 존재하는지 확인합니다.
*
* @return 검색어가 null이 아니고 공백이 아닌 경우 true
*/
public boolean hasSearch() {
return search != null && !search.trim().isEmpty();
}

/**
* 정렬 조건이 존재하는지 확인합니다.
*
* @return 정렬 조건 배열이 null이 아니고, 1개 이상 있는 경우 true
*/
public boolean hasSorters() {
return sorters != null && sorters.length > 0;
}
Expand Down
Loading
Loading