Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
1b072d0
feat : 그라파나용 매트릭 -> 처리시간 p50, p95, p99 추가
waitAmhee Nov 3, 2025
2c09abd
feat : 지연 시간 측정을 위해 stomp 헤더에 타임스탬프 추가
waitAmhee Nov 3, 2025
ed32ae8
feat : 동시 접속자 수 매트릭 추가
waitAmhee Nov 3, 2025
119b1c8
feat : 메시지 유실 측정을 위한 보낸 메시지 수 매트릭 추가
waitAmhee Nov 3, 2025
b0f3036
refactor : WebSocketMetrics와 WebSocketEventListener는 구체적인 기술/라이브러리 사용…
waitAmhee Nov 4, 2025
684436d
fix : Prometheus가 메트릭을 다 가져가고 연결을 닫았는데, 서버는 아직 write 중이라서 Broken pipe…
waitAmhee Nov 4, 2025
bdf7503
refactor: exceptionHandler에서 AsyncRequestNotUsableException 핸들링 안하는 방…
waitAmhee Nov 4, 2025
b77ec0c
fix : 환율 api를 사용해 조회하는 방식에서 레디스에 저장한 값을 읽어오는 방식으로 수정
waitAmhee Nov 4, 2025
11789cf
refactor: 로그 정리
waitAmhee Nov 4, 2025
f293203
fix: 헤더에 sentTimestamp가 담기지 않아서 setNativeHeader로 대체
waitAmhee Nov 4, 2025
4fc1c5d
feat: actuator/metrics 엔드포인트 추가
waitAmhee Nov 5, 2025
c835e83
ehm: jvm에 할당하는 메모리 512Mb로 수정
happyAndy0412 Nov 6, 2025
f4b2f56
ehm: jvm에 할당하는 초기 힙 메모리도 512Mb로 수정
happyAndy0412 Nov 6, 2025
8fce884
ehm: 1. 힙메모리 설정 비교위해 기본으로 복구 2. WebSocketMessageBrokerStats를 사용하여 웹소켓…
happyAndy0412 Nov 7, 2025
9931492
ehm: 메트릭 명명 규칙 위배 사항 수정
happyAndy0412 Nov 7, 2025
89b4d59
ehm: 브로커 로깅 주기 적용안돼서 직접 설정 파일 생성
happyAndy0412 Nov 7, 2025
7b85943
ehm: 메트릭 명명규칙 위반(같은 이름은 같은 타입이어야 함) 재수정
happyAndy0412 Nov 7, 2025
a18adf8
feat: 모듈 분리를 위한 설정 파일 추가 및 수정(websocket 모듈용 application.yml, build.gr…
waitAmhee Nov 10, 2025
29f58bf
refactor: 기존의 코드 infra 모듈에서 websocket 모듈로 이동
waitAmhee Nov 10, 2025
10cb821
feat : LiveStockPriceWebSocketService 클래스 분리
waitAmhee Nov 10, 2025
2f469b5
refactor: 필요없는 코드 삭제 및 수정
waitAmhee Nov 10, 2025
cf61267
refactor: 기존에는 같은 서버 내에서만 이벤트를 발행/구독 -> Redis Pub/Sub을 통해 서버 간 통신이 가능…
waitAmhee Nov 10, 2025
86e9d37
refactor: 서버-클라, 증권사-서버 패키지 분리
waitAmhee Nov 10, 2025
f874696
refactor: 로그정리 및 서버-클라, 증권사-서버 패키지 분리 이후 -> 자동 패키지 리팩토링된 클래스
waitAmhee Nov 10, 2025
3da425e
fix: predestroy 실행 전에 redis가 닫혀버려서 redisTemplate 접근 중 에러 발생 -> Redis …
waitAmhee Nov 10, 2025
1598640
feature: websocket 용 build.gradle파일 추가
waitAmhee Nov 10, 2025
987fc74
refactor: DockerFile 이름 변경
waitAmhee Nov 10, 2025
29e6ade
fix: ecr repository를 fintory로 통일, 태그로 구분
waitAmhee Nov 10, 2025
2947c97
fix: ecr repository를 fintory-child로 통일
waitAmhee Nov 10, 2025
13705a0
fix: application.yml -> 프로필명 통일
waitAmhee Nov 10, 2025
307d8dd
refactor: 필요없는 코드 삭제
waitAmhee Nov 10, 2025
c9be4ba
fix : EC2 간 Bean 의존성 분리 (Service/Repository 직접 참조 제거)
waitAmhee Nov 10, 2025
7ce27f3
fix : child 모듈에서 websocket 모듈 삭제
waitAmhee Nov 11, 2025
9abb802
fix : appcliation.yml 변수 활용 방식 변경
waitAmhee Nov 11, 2025
7e989e2
fix : 들여쓰기 에러 수정
waitAmhee Nov 11, 2025
9c091df
fix : 빈 의존성 수정
waitAmhee Nov 11, 2025
3743bc7
fix : WebSocket 종료 시 Redis 연결 에러 해결
waitAmhee Nov 11, 2025
58fb44c
fix : @EnableJpaAuditing 추가
waitAmhee Nov 11, 2025
eb7fa26
fix : actuator 의존성 추가 + 토큰 발급 주기 23시간 -> 8시간으로 변경
waitAmhee Nov 12, 2025
393e3ab
fix : SecurityConfig 추가 -> 그라파나 대시보드 추가를 위해서.
waitAmhee Nov 12, 2025
0d613a5
fix : dependson 제거
waitAmhee Nov 13, 2025
f3b7092
fix : redis replica 설정 해제
waitAmhee Nov 14, 2025
3b381d5
fix : redis replica 설정 추가(테스트)
waitAmhee Nov 14, 2025
978d9e2
fix : websocket stomp 설정 옮김
waitAmhee Nov 16, 2025
2c63ba5
fix : app-child에서 websocket 모듈 참조 x
waitAmhee Nov 16, 2025
98ce9dd
refactor: 사용하지 않는 코드 삭제
waitAmhee Nov 16, 2025
aea9470
refactor: 중복 stock 데이터 필터링 로직을 테스트를 위해 잠시 주석 처리
waitAmhee Nov 21, 2025
e4569ef
feat: .env 방식으로 deploy-websocket도 변경
waitAmhee Dec 28, 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
101 changes: 101 additions & 0 deletions .github/workflows/deploy-websocket.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
name: Fintory Websocket Module CI/CD
on:
push:
branches: [ "dev" ]
pull_request:
branches: [ "dev" ]

env:
AWS_REGION: ap-northeast-2

jobs:
build-and-push:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v3

- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'temurin'
java-version: '17'

- name: Grant permission to gradlew
run: chmod +x ./gradlew

- name: Cache Gradle
uses: actions/cache@v3
with:
path: |
~/.gradle/caches
~/.gradle/wrapper
key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
restore-keys: |
${{ runner.os }}-gradle-

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: Build and push websocket
run: |
./gradlew :websocket:clean build
docker build -t websocket:latest ./websocket
docker tag websocket:latest ${{ steps.login-ecr.outputs.registry }}/fintory-child:websocket-latest
docker push ${{ steps.login-ecr.outputs.registry }}/fintory-child:websocket-latest

deploy-websocket:
needs: build-and-push
runs-on: ubuntu-latest
steps:
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v2
with:
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-region: ${{ env.AWS_REGION }}

- name: Login to Amazon ECR
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: Deploy fintory-websocket to EC2
uses: appleboy/ssh-action@v1.0.0
with:
host: ${{ secrets.FINTORY_WEBSOCKET_HOST }}
username: ubuntu
key: ${{ secrets.EC2_WEBSOCKET_SSH_KEY }}
script: |
cat > .env << 'EOF'
ECR_REGISTRY=${{ steps.login-ecr.outputs.registry }}
RDS_URL=${{ secrets.RDS_URL }}
RDS_USERNAME=${{ secrets.RDS_USERNAME }}
RDS_PASSWORD=${{ secrets.RDS_PASSWORD }}
AWS_REDIS_HOST=${{ secrets.FINTORY_CHILD_PRIVATE_HOST }}
AWS_REDIS_PASSWORD=${{ secrets.AWS_REDIS_PASSWORD }}
HANTU_APPKEY=${{ secrets.HANTU_APPKEY}}
HANTU_APPSECRET=${{ secrets.HANTU_APPSECRET}}
DB_APPKEY=${{ secrets.DB_APPKEY}}
DB_APPSECRET=${{ secrets.DB_APPSECRET}}
EOS_API_KEY=${{ secrets.EOS_API_KEY}}
EOF


aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin ${{ steps.login-ecr.outputs.registry }}

docker pull ${{ steps.login-ecr.outputs.registry }}/fintory-child:websocket-latest

docker compose down
docker compose up -d

docker image prune -f

5 changes: 3 additions & 2 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -95,15 +95,16 @@ jobs:
export OPENAI_MODEL=${{ secrets.OPENAI_MODEL}}
export OPENAI_TEMPERATURE=${{secrets.OPENAI_TEMPERATURE}}
export OPENAI_MAX_TOKENS=${{secrets.OPENAI_MAX_TOKENS}}
export WEBSOCKET_PRIVATE_HOST=${{secrets.WEBSOCKET_PRIVATE_HOST}}
export FIREBASE_CONFIG='${{secrets.FIREBASE_CONFIG}}'

aws ecr get-login-password --region ap-northeast-2 | \
docker login --username AWS --password-stdin ${{ steps.login-ecr.outputs.registry }}
docker login --username AWS --password-stdin ${{ steps.login-ecr.outputs.registry }}

docker pull ${{ steps.login-ecr.outputs.registry }}/fintory-child:latest

docker compose down
docker compose up -d

docker image prune -f

2 changes: 1 addition & 1 deletion app-child/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ WORKDIR /app
COPY build/libs/*.jar app.jar
COPY src/main/resources/application-deploy.yml /app/application-deploy.yml

ENTRYPOINT ["java", "-jar","app.jar", "--spring.profiles.active=deploy"]
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=deploy"]
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
@SpringBootApplication(scanBasePackages = {
"com.fintory.infra",
"com.fintory.auth",
"com.fintory.child"
"com.fintory.child",
})
@ConfigurationPropertiesScan(basePackages = {
"com.fintory.auth"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@
import com.fintory.domain.stock.dto.korean.response.StockSearchResponse;
import com.fintory.domain.stock.dto.websocket.MarketStatusResponse;
import com.fintory.domain.stock.service.common.CommonStockService;
import com.fintory.domain.stock.service.websocket.LiveStockPriceWebsocketService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.List;

Expand All @@ -20,22 +21,27 @@
public class CommonStockControllerImpl implements CommonStockController {

private final CommonStockService commonStockService;
private final LiveStockPriceWebsocketService websocketService;
private final RestTemplate restTemplate;

@Value("${websocket.server.url}")
private String websocketServerUrl;


//주식 종목 검색
@Override
@GetMapping("/search")
public ResponseEntity<ApiResponse<List<StockSearchResponse>>> searchStock(@RequestParam String keyword) {
List<StockSearchResponse> stockSearchRespons = commonStockService.searchStock(keyword);
return ResponseEntity.ok(ApiResponse.ok(stockSearchRespons));
List<StockSearchResponse> stockSearchResponse = commonStockService.searchStock(keyword);
return ResponseEntity.ok(ApiResponse.ok(stockSearchResponse));
}

//장시간 리턴
@Override
@GetMapping("/opened-market")
public ResponseEntity<ApiResponse<MarketStatusResponse>> getMarketStatus(){
return ResponseEntity.ok(ApiResponse.ok(websocketService.getMarketStatus()));
String url = "http://"+websocketServerUrl+":8080" + "/api/websocket/market/status";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 로직을 websocket 모듈 내에 있는 controller에 정의하지 않으신 이유가 있으신가요

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

처음에는 웹소켓 보다는 일반 장시간을 알려주는 기능이니 주 모듈의 common 컨트롤러에서 처리하는 것이 의미적 일관성이 있다고 판단했는데, 생각해보니 불필요한 HTTP 호출이 발생해서 Websocket 모듈로 옮기는 것이 더 효율적이네요! 수정하도록 하겠습니다! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아 근데 혹시 자주 호출되는 기능이면 현재 방식이 비효율적이라 websocket에 컨트롤러를 직접 추가하는게 나을 것 같은데
아니라면 webSocket 모듈은 Websocket 통신에만 집중하고 API Gateway 역할은 모두 app-child에서 처리하는 게 아키텍쳐 일관성 측면에서 좋은 것 같기도 해서요..! 어떤 방식이 적합할까요?!!?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

api 모듈에서 웹소켓 모듈의 api를 호출하는게 의미적으로도 안맞고 결합도도 증가시킬것같다는 차원에서 그렇게 생각했습니다 차라리 api만 child 모듈로 분리하고 싶으시면 클라이언트에서 openmarketapi 호출하고 반환값으로 웹소켓모듈에 요청하는게 나을 것 같습니다

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네네!! websocket 모듈로 옮기라는 말씀 맞으시죠? 수정하겠습니다! 👍

MarketStatusResponse response = restTemplate.getForObject(url, MarketStatusResponse.class);
return ResponseEntity.ok(ApiResponse.ok(response));
}

}

This file was deleted.

This file was deleted.

Loading
Loading