Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
43 changes: 43 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
{
"permissions": {
"defaultMode": "bypassPermissions",
"allow": [
"Edit(*/demo/**/*)",
"Write(*/demo/**/*)",
"Read(*/demo/**/*)",
"Bash(*/demo/**/*)",
"Bash(./gradlew*)",
"Bash(gradlew*)",
"MultiEdit(*/demo/**/*)",
"LS(*/demo/**/*)",
"Glob(*/demo/**/*)",
"Grep(*/demo/**/*)",
"TodoWrite(*)",
"NotebookEdit(*/demo/**/*)",
"NotebookRead(*/demo/**/*)",
"WebFetch(*)",
"WebSearch(*)",
"Task(*)",
"ExitPlanMode(*)",
"mcp__task-master-ai__*:*"
],
"deny": []
},
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "/Users/gno_root/Desktop/open_the_door/lets_developing/project/whiplash/be/.claude/hooks/stop-automation.sh",
"timeout": 30000
}
]
}
]
}
}



93 changes: 93 additions & 0 deletions .github/workflows/ci-cd-with-dockerhub.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
name: CI-CD Pipeline

on:
push:
branches: [ develop ]

# 각 Job은 독립적인 환경(저장소, 가상머신)에서 실행
jobs:
# 1. 빌드·테스트·이미지 푸시
build:
runs-on: ubuntu-latest
environment: DOCKERHUB_USERNAME

steps:
- name: Check out code
uses: actions/checkout@v3

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin' # AdoptOpenJDK 기반의 Temurin 배포판
java-version: '17' # 설치할 Java 버전
cache: 'gradle' # Gradle 의존성 캐시 활성화 (선택)


- name: Build (no Test)
run: ./gradlew clean bootJar --no-daemon

- name: Log in to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and Push Docker Image
env:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
IMAGE_TAG: ${{ github.sha }}
run: |
# IMAGE_TAG, DOCKERHUB_USERNAME 변수를 사용
docker build \
-t $DOCKERHUB_USERNAME/econoeasy-be:$IMAGE_TAG \
.
docker push \
$DOCKERHUB_USERNAME/econoeasy-be:$IMAGE_TAG

# 2. 빌드 성공 후 배포
deploy:
environment: DOCKERHUB_USERNAME
needs: build
if: github.ref == 'refs/heads/develop'
runs-on: ubuntu-latest

steps:
- name: Check out code # 이 스텝이 있어야 리포지토리 파일에 접근 가능
uses: actions/checkout@v3

- name: Install SSH key
uses: shimataro/ssh-key-action@v2
with:
key: ${{ secrets.SSH_PRIVATE_KEY }}
known_hosts: ${{secrets.SSH_KNOWN_HOSTS}}

- name: Copy docker-compose.yml to EC2
env:
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
DEST_PATH: /home/${{ secrets.SSH_USER }}/econoeasy/docker-compose.yml # EC2의 목적지 경로
run: |
# `docker-compose.yml` 파일이 리포지토리의 루트에 있다고 가정합니다.
# 만약 다른 경로에 있다면 `docker-compose.yml` 대신 해당 경로를 지정하세요.
scp -o StrictHostKeyChecking=no ./docker-compose.yml $SSH_USER@$SSH_HOST:$DEST_PATH


- name: Deploy to EC2 via SSH
env:
IMAGE_TAG: ${{ github.sha }}
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
SSH_USER: ${{ secrets.SSH_USER }}
SSH_HOST: ${{ secrets.SSH_HOST }}
run: |
ssh -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST <<EOF
cd /home/$SSH_USER/econoeasy
# 환경변수 내보내기
export DOCKERHUB_USERNAME=$DOCKERHUB_USERNAME
export IMAGE_TAG=$IMAGE_TAG
# 방금 빌드·푸시된 SHA 태그 이미지를 명시적으로 내려받습니다.
docker pull $DOCKERHUB_USERNAME/econoeasy-be:$IMAGE_TAG
# 도커 컴포즈로 컨테이너를 재시작
docker-compose down
docker-compose up -d
EOF
1 change: 1 addition & 0 deletions env.b64
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
IyBTUFJJTkcNClBST0ZJTEVTX0FDVElWRT1wcm9kDQoNCiMgR01BSUwNCkdNQUlMX0hPU1Q9c210cC5nbWFpbC5jb20NCkdNQUlMX1BPUlQ9NTg3DQpHTUFJTF9VU0VSTkFNRT1ybXNnaGNobDBAZ21haWwuY29tDQpHTUFJTF9QQVNTV09SRD1rdXphbWhseGJsamFwdG9tDQoNCiMgTVlTUUwNClJEQl9VUkw9amRiYzpteXNxbDovL3Jkcy1lY29ub2Vhc3kuYzN5Z2V1Z3dtNDVsLmFwLW5vcnRoZWFzdC0yLnJkcy5hbWF6b25hd3MuY29tOjMzMDYvZWNvbm9lYXN5DQpSREJfVVNFUk5BTUU9YWRtaW4NClJEQl9QQVNTV09SRD1xbHFqczEzNQ0KUkRCX0RSSVZFUl9DTEFTU19OQU1FPWNvbS5teXNxbC5jai5qZGJjLkRyaXZlcg0KDQojIG1vbmdvZGINCk1PTkdPX1VSST1tb25nb2RiK3NydjovL2FkbWluOnFscWpzMTM1QG1vbmdvZGJmb3JlY29ub2Vhc3kuNmxlbmtsci5tb25nb2RiLm5ldC9lY29ub2Vhc3k/cmV0cnlXcml0ZXM9dHJ1ZSZ3PW1ham9yaXR5JmFwcE5hbWU9bW9uZ29kYkZvckVjb25vZWFzeQ0KTU9OR09fQVVUSEVOVElDQVRJT05fREFUQUJBU0U9YWRtaW4NCg0KIyBSRURJUw0KUkVESVNfSE9TVD1tYXN0ZXIucmVkaXMtZWNvbm9lYXN5LnI2Z3RvMS5hcG4yLmNhY2hlLmFtYXpvbmF3cy5jb20NClJFRElTX1BPUlQ9NjM3OQ0KUkVESVNfU1RSRUFNX0tFWT1hcnRpY2xlLXN0cmVhbQ0KUkVESVNfREVEVVBfS0VZPXByb2Nlc3NlZC1hcnRpY2xlcw0KUkVESVNfU1NMX0VOQUJMRUQ9dHJ1ZQ0KDQpSRURJU19LRVlXT1JEX1JFQ09NTUVORF9DQUNIRV9QUkVGSVg9eW91dHViZTprZXl3b3JkOg0KUkVESVNfQ09NTU9OX1JFQ09NTUVORF9DQUNIRV9LRVk9eW91dHViZTpjb21tb24NCg0KUkVESVNfUkVDT01NRU5EX1NUUkVBTV9LRVk9eW91dHViZS1yZWNvbW1lbmQtc3RyZWFtDQpSRURJU19SRUNPTU1FTkRfUkVTVUxUX1NUUkVBTV9LRVk9eW91dHViZS1yZWNvbW1lbmQtcmVzdWx0LXN0cmVhbQ0KUkVESVNfUkVDT01NRU5EX1JFU1VMVF9DT05TVU1FUl9HUk9VUD15b3V0dWJlLXJlY29tbWVuZC1jb25zdW1lci1ncm91cA0KUkVESVNfUkVDT01NRU5EX1JFU1VMVF9DT05TVU1FUl9OQU1FPXlvdXR1YmUtcmVjb21tZW5kLWNvbnN1bWVyDQoNClJFRElTX0FSVElDTEVfS0VZV09SRF9TVFJFQU1fS0VZPWFydGljbGUta2V5d29yZC1yZXN1bHQtc3RyZWFtDQpSRURJU19BUlRJQ0xFX0tFWVdPUkRfQ09OU1VNRVJfR1JPVVA9YXJ0aWNsZS1rZXl3b3JkLWNvbnN1bWVyLWdyb3VwDQpSRURJU19BUlRJQ0xFX0tFWVdPUkRfQ09OU1VNRVJfTkFNRT1hcnRpY2xlLWtleXdvcmQtY29uc3VtZXINCg0KIyBKV1QNCkpXVF9TRUNSRVQ9WW1sdVlYSjVjMlZqY21WMGEyVjVkR2hoZEdsemRtVnllV3h2Ym1kaGJtUnpaV04xY21VPQ0KSldUX0FDQ0VTU19FWFBJUkFUSU9OPTEwMDANCkpXVF9SRUZSRVNIX0VYUElSQVRJT049MTAwMDAwDQpKV1RfSVNTVUVSPWFzZGYNCg0KS0FLQU9fQ0xJRU5UX0lEPTFiZGMzYjMxOWJlMzg2ODczNDBiNjYxMWI5ODFjZDkzDQpLQUtBT19SRURJUkVDVF9VUkk9YXNkZg0KDQojIEFXUw0KQVdTX0FDQ0VTU19LRVlfSUQ9QUtJQVhLUFVaTDRRVlQ0V0VFNjINCkFXU19TRUNSRVRfQUNDRVNTX0tFWT1rNGVxYTU0ejZza21WSmxYaUNHMy81RVhTSC93K3pVR2NEQVVhaVg5DQpBV1NfUkVHSU9OPWFwLW5vcnRoZWFzdC0yDQoNCkVDUl9SRUdJU1RSWT01MDM1NjE0MTIzODUuZGtyLmVjci5hcC1ub3J0aGVhc3QtMi5hbWF6b25hd3MuY29tDQpFQ1JfUkVQT1NJVE9SWT1lY29ub2Vhc3kvZWNvbm9lYXN5LWJlDQpFQ1NfQ0xVU1RFUj1jbHVzdGVyLWVjb25vZWFzeQ0KRUNTX1NFUlZJQ0U9dGFzay1mYW1pbHktZWNvbm9lYXN5LXNlcnZpY2UtenIzNW5qbHUNCkVDU19UQVNLX0ZBTUlMWT10YXNrLWZhbWlseS1lY29ub2Vhc3kNCg0K
9 changes: 9 additions & 0 deletions http/ex.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"ageRange": "THIRTIES",
"investmentLevel": "BEGINNER",
"riskTolerance": "MODERATE",
"investmentGoal": "LONG_TERM_GROWTH",
"interestCategories": [
"STEEL"
]
}
166 changes: 154 additions & 12 deletions http/summariaztion.http
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,161 @@ Content-Type: application/json

{
"articleIds": [
"68f3573a6313854f303cbf76",
"68f3573a6313854f303cbf7b",
"68f3573a6313854f303cbf7a",
"68f3573a6313854f303cbf79",
"68f3573a6313854f303cbf78",
"68f3573a6313854f303cbf77",
"68f32d0a339aa75e7f6ea8a0",
"68f32d0a339aa75e7f6ea8a2",
"68f32d0a339aa75e7f6ea8a1"
],
"timestamp": "2025-10-18T23:50:00"
"690eef2c19493dfd91815ac4",
"690eef2c19493dfd91815ac5",
"690eda158b4b6a13178f88c1",
"690eda168b4b6a13178f88c3",
"690eda168b4b6a13178f88c2"

]
,
"timestamp": "2025-10-30T23:50:00"
}


### health check
GET api.econoeasy.xyz/health
GET https://api.econoeasy.xyz/health

### 로그인 (AccessToken 자동 저장)
POST http://api.econoeasy.xyz/api/login
Content-Type: application/json

{
"email": "rmsghchl0@gmail.com",
"password": "1111"
}

> {%
client.test("로그인 성공", function() {
client.assert(response.status === 200, "응답 상태가 200이어야 합니다");
});

// accessToken을 전역 변수로 저장
if (response.body.data && response.body.data.accessToken) {
client.global.set("accessToken", response.body.data.accessToken);
console.log("AccessToken이 환경변수에 저장되었습니다:", response.body.data.accessToken);
}
%}









### 로컬 테스트
POST http://localhost:8080/api/articles/summarization/request
Content-Type: application/json

{
"articleIds":[
"68d164b80c7d43f45ae3ef39"

],
"timestamp": "2025-10-19T20:00:00"
}

### 로그인 (AccessToken 자동 저장)
POST http://localhost:8080/api/login
Content-Type: application/json

{
"email": "rmsghchl0@gmail.com",
"password": "1111"
}

> {%
client.test("로그인 성공", function() {
client.assert(response.status === 200, "응답 상태가 200이어야 합니다");
});

// accessToken을 전역 변수로 저장
if (response.body.data && response.body.data.accessToken) {
client.global.set("accessToken", response.body.data.accessToken);
console.log("AccessToken이 환경변수에 저장되었습니다:", response.body.data.accessToken);
}
%}


### 유튜브 영상 추천 (인증 필요)
GET https://api.econoeasy.xyz/api/recommends/videos/youtube

### 유튜브 영상 추천 - 로컬
GET http://localhost:8080/api/recommends/videos/youtube
## Authorization: {{accessToken}}
###
GET https://api.econoeasy.xyz/health
###
GET http://localhost:8080/api/recommends/articles
Authorization: Bearer {{accessToken}}

###
GET http://localhost:8080/health

###
GET localhost:8080/api/articles/summarized

###
GET localhost:8080/api/learning/streak
Authorization: Bearer {{accessToken}}

###
POST localhost:8080/api/quiz/results
Authorization: Bearer {{accessToken}}
Content-Type: application/json

{
"results": [
{
"question": "국내 시중은행의 주요 기능으로 가장 적절한 것은 무엇인가요?",
"options": [
"국가 통화 발행 및 관리",
"정부의 외환보유액 운용",
"일반 개인 및 기업에 예금 수취 및 대출 제공",
"기준금리 결정 및 발표"
],
"answerIndex": 2,
"userAnswerIndex": 2,
"explanation": "시중은행은 일반 대중으로부터 예금을 받아 자금을 조달하고, 이를 다시 개인이나 기업에 대출해주는 것이 핵심 기능입니다. 통화 발행 및 기준금리 결정은 중앙은행의 역할입니다.",
"term": "기타"
},
{
"question": "한국은행이 기준금리를 인상했을 때, 국내 시중은행들이 일반적으로 취하는 조치로 가장 거리가 먼 것은 무엇인가요?",
"options": [
"예금 금리 인상",
"대출 금리 인상",
"가계 및 기업 대출 심사 강화",
"통화량 증대를 위한 신규 대출 확대"
],
"answerIndex": 3,
"userAnswerIndex": 3,
"explanation": "기준금리 인상은 시중 유동성을 흡수하고 물가 안정을 도모하는 목적이 있습니다. 따라서 시중은행은 예금 및 대출 금리를 인상하고 대출 심사를 강화하여 통화량 증가를 억제하는 방향으로 움직입니다. 신규 대출 확대는 기준금리 인상 목적과 상반됩니다.",
"term": "기타"
},
{
"question": "다음 중 국내 시중은행에 대한 설명으로 옳지 않은 것은 무엇인가요?",
"options": [
"금융소비자 보호를 위해 금융감독원의 감독을 받는다.",
"주로 영리 추구를 목적으로 다양한 금융 서비스를 제공한다.",
"최종 대부자(Lender of Last Resort)로서 금융 시스템 위기 시 유동성을 공급한다.",
"디지털 전환 가속화로 모바일 뱅킹 등 비대면 서비스가 강화되고 있다."
],
"answerIndex": 2,
"userAnswerIndex": 1,
"explanation": "최종 대부자(Lender of Last Resort)로서 금융 시스템 위기 시 유동성을 공급하는 역할은 중앙은행인 한국은행의 주요 기능입니다. 시중은행은 이 역할을 수행하지 않습니다.",
"term": "기타"
}
]
}


###
GET localhost:8080/api/articles/recently-viewed?date=2025-11-26
Authorization: Bearer {{accessToken}}


###
GET api.econoeasy.xyz/api/articles/recently-viewed?date=2025-11-27
Authorization: Bearer {{accessToken}}
Loading