Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e2fb899
[CICD] release 배포 (#46)
99hyuk Apr 27, 2025
ec8e049
[REFACT] cd_workflow 파일 수정
99hyuk Apr 27, 2025
ddf875c
[REFACT] cd_workflow 파일 수정
99hyuk Apr 27, 2025
9b87fb6
[REFACT] cd_workflow 파일 수정
99hyuk Apr 27, 2025
0e8d63d
[REFACT] cd_workflow 파일 수정
99hyuk Apr 27, 2025
976f7ff
[REFACT] DockerFile 이미지에 curl 설치
99hyuk Apr 27, 2025
78d1b02
[REFACT] cd_workflow 파일 헬스체크 시간 확대
99hyuk Apr 27, 2025
d7f4d6f
[REFACT] dockerfile 자바 이미지 변경
99hyuk Apr 27, 2025
034993f
[REFACT] SecurityConfig에 인증되지 않은 사용자도 healthcheck에 접근할 수 있또록 수정
99hyuk Apr 27, 2025
4858c7b
[REFACT] 스웨거도 접속 가능하도록 수정
99hyuk Apr 27, 2025
6626c4e
[REFACT] CORS 설정 수정
99hyuk Apr 27, 2025
9438986
[REFACT] 도커파일 헬스체크 지연시간 추가
99hyuk Apr 27, 2025
c0c7798
[REFACT] cd_workflow 헬스체크 시간 추가
99hyuk Apr 27, 2025
335dfe8
[TEST] 테스트 위해 인증 로직 임시 수정
99hyuk Apr 27, 2025
e1e17cc
[TEST] 테스트 위해 SecurityConfig 임시 수정 (최신 브랜치 기준)
99hyuk Apr 27, 2025
095c6b5
[REFACT] Discord 알림 폴링 시간 100초 -> 150초로 증가
99hyuk Apr 27, 2025
454d9c3
배포 prod.yml ddl-auto: create -> update로 수정
99hyuk Apr 27, 2025
f3f71d4
[REFACT] docker HealthCheck 개선 (작동 시간 증가)
99hyuk Apr 27, 2025
914d795
[REFACT] 배포 후 빌드 과정에 캐시 사용하지 않도록 수정
99hyuk Apr 27, 2025
952aebb
[REFACT] 도커파일 설정에서 어떤 .jar 파일을 빌드할 지 명확히 설정
99hyuk Apr 27, 2025
b836b16
[REFACT] cd 배포 파일에서 도커 이미지 정리, 도커 헬스체크 의존성 포함 여부 검증 로직 추가
99hyuk Apr 27, 2025
f003b3d
[FIX] localhost는 러너 기준으로 문제가 있어 EC2 기준으로 디스코드 배포 알림 URL 수정
99hyuk Apr 27, 2025
1912fdd
[FIX] Authorization 헤더 노출 가능하도록 수정, 쿠키 자격 증명 비활성화되도록 CORS 수정
99hyuk Apr 28, 2025
3bbb145
[FIX] SelfDiagnosis 조회 시 createdAt null 방어 처리 및 Update 메소드 리턴 제거
99hyuk Apr 28, 2025
81ad1c2
[REFACT] SpringSecurity 내 basic 인증 사용하지 않도록 설정, 세션 사용하지 않도록 설정
99hyuk Apr 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
133 changes: 66 additions & 67 deletions .github/workflows/release_cd_workflow.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,24 @@ jobs:
- name: Grant execute permission for gradlew
run: chmod +x gradlew

- name: Gradle Caching
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: Gradle Caching
# 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: Build jar
run: ./gradlew --info clean bootJar -x test
run: ./gradlew clean --refresh-dependencies --no-build-cache --no-daemon bootJar -x test #./gradlew --info clean bootJar -x test

# Actuator 포함 여부 검증
- name: Assert actuator present
run: |
jar tf build/libs/ouch.jar | grep spring-boot-actuator || {
echo "❌ Actuator not in JAR"; exit 1; }

- name: docker login
uses: docker/login-action@v3
Expand All @@ -45,6 +51,8 @@ jobs:
context: .
push: true
tags: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPOSITORY }}:latest
no-cache: true # 💡 매 빌드마다 새 이미지
pull: true # 💡 베이스 이미지도 최신으로

deploy:
runs-on: ubuntu-latest
Expand All @@ -58,16 +66,10 @@ jobs:
username: ${{ secrets.EC2_USER }}
key: ${{ secrets.EC2_SSH_KEY }}
script: |
# secrets를 미리 bash 변수에 담는다
DOCKER_USERNAME=${{ secrets.DOCKER_HUB_USERNAME }}
DOCKER_REPO=${{ secrets.DOCKER_HUB_REPOSITORY }}
RDS_HOST=${{ secrets.RDS_HOST }}
RDS_PORT=${{ secrets.RDS_PORT }}
RDS_DB=${{ secrets.RDS_DB }}
RDS_USERNAME=${{ secrets.RDS_USERNAME }}
RDS_PASSWORD=${{ secrets.RDS_PASSWORD }}
JWT_SECRET=${{ secrets.JWT_SECRET }}
OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}
# 디스크 청소 두 줄 / Docker 이미지, 로그 정리
docker system prune -af || true
docker builder prune -af
docker volume prune -f

# docker-compose 설치 여부 확인, 없으면 설치
if ! command -v docker-compose &> /dev/null
Expand All @@ -76,55 +78,51 @@ jobs:
sudo apt-get install -y docker-compose-plugin
fi

# Docker 이미지 정리
docker system prune -af || true

# 최신 이미지 pull
docker pull $DOCKER_USERNAME/$DOCKER_REPO:latest
docker pull ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPOSITORY }}:latest

# docker-compose.yml 생성
cat <<EOF > /home/ubuntu/docker-compose.yml
version: '3'
services:
app:
image: $DOCKER_USERNAME/$DOCKER_REPO:latest
container_name: app
restart: always
ports:
- '8080:8080'
env_file:
- .env
read_only: true
tmpfs:
- /tmp
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/actuator/health"]
interval: 30s
timeout: 5s
retries: 3
EOF

echo "version: '3'" > /home/ubuntu/docker-compose.yml
echo "services:" >> /home/ubuntu/docker-compose.yml
echo " app:" >> /home/ubuntu/docker-compose.yml
echo " image: ${{ secrets.DOCKER_HUB_USERNAME }}/${{ secrets.DOCKER_HUB_REPOSITORY }}:latest" >> /home/ubuntu/docker-compose.yml
echo " container_name: app" >> /home/ubuntu/docker-compose.yml
echo " restart: always" >> /home/ubuntu/docker-compose.yml
echo " ports:" >> /home/ubuntu/docker-compose.yml
echo " - '8080:8080'" >> /home/ubuntu/docker-compose.yml
echo " env_file:" >> /home/ubuntu/docker-compose.yml
echo " - .env" >> /home/ubuntu/docker-compose.yml
echo " read_only: true" >> /home/ubuntu/docker-compose.yml
echo " tmpfs:" >> /home/ubuntu/docker-compose.yml
echo " - /tmp" >> /home/ubuntu/docker-compose.yml
echo " healthcheck:" >> /home/ubuntu/docker-compose.yml
echo " test: [\"CMD\",\"curl\",\"-f\",\"http://localhost:8080/actuator/health\"]" >> /home/ubuntu/docker-compose.yml
echo " interval: 30s" >> /home/ubuntu/docker-compose.yml
echo " timeout: 5s" >> /home/ubuntu/docker-compose.yml
echo " retries: 5" >> /home/ubuntu/docker-compose.yml
echo " start_period: 60s" >> /home/ubuntu/docker-compose.yml

# .env 파일 생성
cat <<EOF > /home/ubuntu/.env
SPRING_PROFILES_ACTIVE=prod
RDS_HOST=$RDS_HOST
RDS_PORT=$RDS_PORT
RDS_DB=$RDS_DB
RDS_USERNAME=$RDS_USERNAME
RDS_PASSWORD=$RDS_PASSWORD
JWT_SECRET=$JWT_SECRET
OPENAI_API_KEY=$OPENAI_API_KEY
EOF
echo "SPRING_PROFILES_ACTIVE=prod" > /home/ubuntu/.env
echo "RDS_HOST=${{ secrets.RDS_HOST }}" >> /home/ubuntu/.env
echo "RDS_PORT=${{ secrets.RDS_PORT }}" >> /home/ubuntu/.env
echo "RDS_DB=${{ secrets.RDS_DB }}" >> /home/ubuntu/.env
echo "RDS_USERNAME=${{ secrets.RDS_USERNAME }}" >> /home/ubuntu/.env
echo "RDS_PASSWORD=${{ secrets.RDS_PASSWORD }}" >> /home/ubuntu/.env
echo "JWT_SECRET=${{ secrets.JWT_SECRET }}" >> /home/ubuntu/.env
echo "OPENAI_API_KEY=${{ secrets.OPENAI_API_KEY }}" >> /home/ubuntu/.env

# .env 파일 권한 설정
chmod 600 /home/ubuntu/.env


# 기존 app 컨테이너 강제 제거 (없어도 에러 무시)
docker rm -f app || true

# docker-compose pull
docker compose -f /home/ubuntu/docker-compose.yml pull

# docker-compose 재배포
docker compose -f /home/ubuntu/docker-compose.yml down || true
docker compose -f /home/ubuntu/docker-compose.yml up -d
docker compose -f /home/ubuntu/docker-compose.yml up -d --force-recreate --remove-orphans

- name: Send Discord Notification
if: always()
Expand All @@ -136,14 +134,15 @@ EOF
EMOJI="❌"
fi

# 서버 Healthcheck
sleep 5
HEALTH=$(curl -s -o /dev/null -w "%{http_code}" http://localhost:8080/actuator/health || true)
if [ "$HEALTH" == "200" ]; then
HEALTH_STATUS="✅ 서버 정상 작동"
else
HEALTH_STATUS="❌ 서버 비정상 작동"
fi
# 반복 폴링 헬스체크
HEALTH_STATUS="❌ 서버 비정상 작동"
for i in {1..30}; do
if curl -sSf https://ouchapi.duckdns.org/actuator/health >/dev/null; then
HEALTH_STATUS="✅ 서버 정상 작동"
break
fi
sleep 5
done

MESSAGE="$EMOJI **Ouch 배포 결과**\\n상태: $STATUS\\n$HEALTH_STATUS\\n🔗 프로젝트: ${{ github.repository }}\\n👤 커밋: ${{ github.actor }}"

Expand Down
11 changes: 7 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
FROM openjdk:17
FROM openjdk:17-slim

ARG JAR_FILE=build/libs/*.jar
# curl 설치 (헬스체크용) openjdk:17 (slim버전이 아닌 full 버전)로 설치하고 RUN줄 삭제 해도 됨
RUN apt-get update && apt-get install -y curl

ARG JAR_FILE=build/libs/ouch.jar

COPY ${JAR_FILE} ouch.jar

ENTRYPOINT ["java", "-jar", "ouch.jar"]

HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -f http://localhost:8080/actuator/health || exit 1
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=5 \
CMD status=$(curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/actuator/health) && [ "$status" -eq 200 ]
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,13 @@
import com.onebridge.ouch.domain.mapping.DiagnosisSymptom;
import com.onebridge.ouch.dto.selfDiagnosis.request.DiagnosisCreateRequest;
import com.onebridge.ouch.dto.selfDiagnosis.request.DiagnosisUpdateRequest;
import com.onebridge.ouch.dto.selfDiagnosis.response.DiagnosisUpdateResponse;
import com.onebridge.ouch.dto.selfDiagnosis.response.GetDiagnosisByUserIdResponse;
import com.onebridge.ouch.dto.selfDiagnosis.response.GetDiagnosisResponse;
import com.onebridge.ouch.dto.selfDiagnosis.response.GetSymptomsOfDiagnosisResponse;

@Component
public class SelfDiagnosisConverter {

public DiagnosisUpdateResponse diagnosisToDiagnosisUpdateResponse(SelfDiagnosis updatedDiagnosis) {
List<String> symptoms = symptomListForResponseDto(updatedDiagnosis);
return new DiagnosisUpdateResponse(updatedDiagnosis.getId(), updatedDiagnosis.getVisitType(), symptoms,
updatedDiagnosis.getDuration(), updatedDiagnosis.getPainSeverity(), updatedDiagnosis.getAdditionalNote(),
updatedDiagnosis.getCreatedAt().toString());
}

public GetDiagnosisResponse diagnosisToGetDiagnosisResponse(SelfDiagnosis diagnosis) {
List<String> symptoms = symptomListForResponseDto(diagnosis);
return new GetDiagnosisResponse(diagnosis.getUser().getId(), diagnosis.getVisitType(), symptoms,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
@Getter
public class DiagnosisCreateRequest {

// @NotNull(message = "User Id is required.")
// private Long userId;

@NotNull(message = "Visit type is required.")
private VisitType visitType;

Expand Down

This file was deleted.

19 changes: 11 additions & 8 deletions src/main/java/com/onebridge/ouch/security/SecurityConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,18 @@

import java.util.List;

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;

import com.onebridge.ouch.security.filter.JwtAuthenticationFilter;
import com.onebridge.ouch.security.tokenManger.TokenManager;
Expand All @@ -35,14 +34,15 @@ public SecurityFilterChain publicResourceConfig(HttpSecurity http) throws Except
http.cors(
cors -> cors.configurationSource(corsConfigurationSource())
);
http.sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)); // 세션 사용 안함
http.addFilterAt(new JwtAuthenticationFilter(tokenManager), BasicAuthenticationFilter.class);
http.authorizeHttpRequests(
(authorizeRequests)
-> authorizeRequests.requestMatchers("/users/login", "/users/signup/**").permitAll() // 로그인, 회원가입 페이지는 모두 허용
.anyRequest().authenticated() // 그 외의 요청은 인증 필요
// -> authorizeRequests.anyRequest().permitAll() // 모든 사용자 접근 가능
// -> authorizeRequests.anyRequest().authenticated() // 로그인한 사용자만 접근 가능
);
-> authorizeRequests.anyRequest().permitAll());
// -> authorizeRequests.requestMatchers("/users/login", "/users/signup/**", "/actuator/health", "/health",
// "/swagger-ui/**", "/v3/api-docs/**").permitAll() // 로그인, 회원가입 페이지는 모두 허용
// .anyRequest().authenticated() // 그 외의 요청은 인증 필요
// );
return http.build();
}

Expand All @@ -56,7 +56,10 @@ public CorsConfigurationSource corsConfigurationSource() {
configuration.addAllowedHeader("*");
configuration.addAllowedMethod("*");

configuration.setAllowCredentials(true);
configuration.setAllowCredentials(false);

// ***응답 헤더 노출***
configuration.setExposedHeaders(List.of("Authorization", "Refresh")); // 필요하면 추가

UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
source.registerCorsConfiguration("/**", configuration);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.onebridge.ouch.dto.selfDiagnosis.request.AddSymptomsToDiagnosisRequest;
import com.onebridge.ouch.dto.selfDiagnosis.request.DiagnosisCreateRequest;
import com.onebridge.ouch.dto.selfDiagnosis.request.DiagnosisUpdateRequest;
import com.onebridge.ouch.dto.selfDiagnosis.response.DiagnosisUpdateResponse;
import com.onebridge.ouch.dto.selfDiagnosis.response.GetDiagnosisByUserIdResponse;
import com.onebridge.ouch.dto.selfDiagnosis.response.GetDiagnosisResponse;
import com.onebridge.ouch.dto.selfDiagnosis.response.GetSymptomsOfDiagnosisResponse;
Expand Down Expand Up @@ -110,7 +109,7 @@ public GetSymptomsOfDiagnosisResponse getSymptomsOfDiagnosis(Long diagnosisId, L

//자가진단표 수정
@Transactional
public DiagnosisUpdateResponse updateDiagnosis(Long diagnosisId, Long userId, DiagnosisUpdateRequest request) {
public void updateDiagnosis(Long diagnosisId, Long userId, DiagnosisUpdateRequest request) {
SelfDiagnosis diagnosis = selfDiagnosisRepository.findByIdAndUserId(diagnosisId, userId)
.orElseThrow(() -> new OuchException(DiagnosisErrorCode.DIAGNOSIS_NOT_FOUND));

Expand Down Expand Up @@ -139,8 +138,6 @@ public DiagnosisUpdateResponse updateDiagnosis(Long diagnosisId, Long userId, Di
}

selfDiagnosisRepository.save(updatedDiagnosis);

return selfDiagnosisConverter.diagnosisToDiagnosisUpdateResponse(updatedDiagnosis);
}

//특정 자가진단표에 증상 추가
Expand Down
2 changes: 1 addition & 1 deletion src/main/resources/application-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ spring:
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
ddl-auto: create
ddl-auto: update
show-sql: true
properties:
hibernate:
Expand Down