diff --git a/.github/workflows/ci-docker.yml b/.github/workflows/ci-docker.yml index 72c0235..21b66e8 100644 --- a/.github/workflows/ci-docker.yml +++ b/.github/workflows/ci-docker.yml @@ -3,6 +3,8 @@ name: IssueDive Docker CI on: push: branches: [ "dev" ] # dev 브랜치에 푸시될 때 실행 + pull_request: + branches: [ "dev" ] # PR 보낼 때도 실행 jobs: build-and-push: diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 83aa4d0..a1fb71a 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -28,8 +28,15 @@ jobs: docker pull ${{ secrets.DOCKERHUB_USERNAME }}/issuedive:latest # 3. 기존에 실행 중인 앱 컨테이너가 있다면 중지하고 삭제 (최초 실행 시 오류가 나지 않도록 || true 추가) - docker stop issuedive-app || true - docker rm issuedive-app || true + # docker stop issuedive-app || true + # docker rm issuedive-app || true + # -> 8080 포트를 사용하는 기존 컨테이너를 찾아 중지 및 삭제 로직으로 변경 + # 컨테이너 이름과 상관없이 포트 기준으로 찾기 때문에 더 안정적 + CONTAINER_ID=$(docker ps -q --filter "publish=8080") + if [ -n "$CONTAINER_ID" ]; then + docker stop $CONTAINER_ID + docker rm $CONTAINER_ID + fi # 4. 최신 이미지로 새로운 앱 컨테이너 실행 # 이전에 생성한 Docker 네트워크와 .env 파일을 사용합니다. diff --git a/src/main/java/com/issueDive/config/SecurityConfig.java b/src/main/java/com/issueDive/config/SecurityConfig.java index 7b6bb64..dc98f4d 100644 --- a/src/main/java/com/issueDive/config/SecurityConfig.java +++ b/src/main/java/com/issueDive/config/SecurityConfig.java @@ -1,6 +1,7 @@ package com.issueDive.config; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpMethod; @@ -8,14 +9,9 @@ import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; 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.http.SessionCreationPolicy; -import org.springframework.security.core.userdetails.User; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; import org.springframework.security.web.SecurityFilterChain; import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; import org.springframework.web.cors.CorsConfiguration; @@ -32,12 +28,12 @@ @RequiredArgsConstructor public class SecurityConfig { - private final CustomUserDetailsService userDetailsService; // DB 기반 인증 private final JwtAuthenticationFilter jwtAuthenticationFilter; // JWT 필터 + @Value("${cors.allowed-origins}") + private String allowedOrigins; // 공개적으로 접근 가능한 URL 목록 - // private static final String[] PUBLIC_URLS = { "/swagger-ui/**", "/v3/api-docs/**", @@ -69,11 +65,9 @@ public CorsConfigurationSource corsConfigurationSource() { CorsConfiguration configuration = new CorsConfiguration(); // 프론트엔드 서버 주소 허용 - configuration.setAllowedOrigins(Arrays.asList("http://localhost:5173", "http://localhost:5174")); + configuration.setAllowedOrigins(Arrays.asList(allowedOrigins.split(","))); // 모든 HTTP 메서드 허용 configuration.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS")); - // 모든 헤더 허용 -// configuration.setAllowedHeaders(Arrays.asList("Origin", "Content-Type", "Accept", "Authorization")); configuration.addAllowedHeader("*"); // 자격 증명(쿠키 등) 허용 configuration.setAllowCredentials(true); @@ -88,19 +82,6 @@ public CorsConfigurationSource corsConfigurationSource() { public PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } - - /* - @Bean - public UserDetailsService userDetailsService(PasswordEncoder passwordEncoder) { - UserDetails user = User.withUsername("test") - .password(passwordEncoder.encode("test")) - .roles("USER") - .build(); - return new InMemoryUserDetailsManager(user); - } - - */ - @Bean public AuthenticationManager authenticationManager(AuthenticationConfiguration config) throws Exception { return config.getAuthenticationManager(); diff --git a/src/main/resources/application.properties.example b/src/main/resources/application.properties.example new file mode 100644 index 0000000..42fe688 --- /dev/null +++ b/src/main/resources/application.properties.example @@ -0,0 +1,53 @@ +# =================================================================== +# Example Configuration +# ------------------------------------------------------------------- +# ? ??? ???? 'application.properties' ??? ??? ?, +# ??? ?? ??? ?? ?? <...> ??? ?????. +# 'application.properties' ??? .gitignore? ??? ????? ???. +# =================================================================== + +# --- Database (MySQL) Settings --- +# ?????? ??? ?? URL, ??? ??, ????? ?????. +spring.datasource.url=jdbc:mysql://localhost:3306/issue_dive?useSSL=false&serverTimezone=Asia/Seoul&allowPublicKeyRetrieval=true +spring.datasource.username= +spring.datasource.password= +spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver + +# --- JPA & Hibernate Settings --- +spring.jpa.hibernate.ddl-auto=none +spring.jpa.show-sql=true +spring.jpa.properties.hibernate.format_sql=true +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect +# spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect + +# --- Hikari Connection Pool Settings --- +spring.datasource.hikari.connection-test-query=SELECT 1 +spring.datasource.hikari.maximum-pool-size=10 + +# --- Flyway Migration Settings --- +spring.flyway.enabled=true +spring.flyway.locations=classpath:db/migration +spring.flyway.baseline-on-migrate=true + +# --- CORS Settings for Local Development --- +# ?? ?? ? ??? ????? ?????. +cors.allowed-origins=http://localhost:5173, http://localhost:5174 + +# --- Application Logging Settings --- +logging.level.root=INFO +logging.level.org.hibernate.SQL=DEBUG +logging.level.org.hibernate.type.descriptor.sql.BasicBinder=TRACE +logging.level.org.springframework.web=DEBUG +logging.level.com.example.issueDive=DEBUG +logging.file.name=logs/issueDive.log +logging.pattern.file=%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n + +# --- Application Settings --- +spring.application.name=issueDive + +# --- Jackson --- +# spring.jackson.date-format=yyyy-MM-dd HH:mm:ss +# spring.jackson.serialization.WRITE_DATES_AS_TIMESTAMPS=false + +# --- K6 performance test Profile --- +#spring.profiles.active=performance \ No newline at end of file