Skip to content
Open
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
761d3dd
🐛 Fix: Railway CLI 액션을 npm 설치 방식으로 변경
jun108059 Sep 19, 2025
2e6fa85
🐛 Fix: TestCode 수정
jun108059 Sep 19, 2025
849587a
🐛 Fix: railway login추가
jun108059 Sep 19, 2025
23faea3
🐛 Fix: railway token 수정
jun108059 Sep 19, 2025
799c11e
🐛 Fix: railway login 다시 추가
jun108059 Sep 19, 2025
c74c7b6
🐛 Fix: Dockerfile로 빌드하도록 수정
jun108059 Sep 20, 2025
abad735
🐛 Fix: project argument 제거
jun108059 Sep 20, 2025
4ef3af5
🐛 Fix: service id 수정
jun108059 Sep 20, 2025
f0502b6
🐛 Fix: TOKEN env 수정
jun108059 Sep 20, 2025
93fd89b
⚙️Add: ADMIN 배포를 위한 github action
jun108059 Sep 20, 2025
6c80307
🐛 Fix: env 설정 변경
jun108059 Sep 20, 2025
c2a3422
🐛 Fix: node nev 설정과 빌드 에러 해결
jun108059 Sep 20, 2025
c41e44d
🐛 Fix: node nev 설정과 빌드 에러 해결
jun108059 Sep 20, 2025
4450bd9
⚙️ Update: 자동 배포 제거(수동 배포)
jun108059 Sep 21, 2025
e943040
⚙️ Update: Production 환경 workflow 수정
jun108059 Sep 21, 2025
199e806
⚙️ Update: Swagger server URL 환경별 적용 추가
jun108059 Sep 21, 2025
7a7220f
🐛 Fix: application.yml 설정
jun108059 Sep 21, 2025
56e41a6
✨ Add: 어드민 기능 - 스키장 슬포르 업데이트 기능 추가
jun108059 Nov 29, 2025
d3dcf55
✨ Add: 어드민 기능 - 스키장 슬포르 업데이트 기능 추가
jun108059 Nov 29, 2025
05b9669
Merge pull request #3 from jun108059/feature/admin-slope-update
jun108059 Nov 29, 2025
d8f347a
🐛 Fix: page 무한 로딩 에러 수정
jun108059 Nov 29, 2025
a5a0cb0
⚙️ Update: App version 강제 업데이트 3.1.2
jun108059 Dec 3, 2025
bcdc6d4
Merge pull request #5 from jun108059/feature/app-version-up
jun108059 Dec 3, 2025
597e276
Merge pull request #4 from jun108059/feature/admin-slope-update
jun108059 Dec 3, 2025
043baa1
✨ 웹캠 조회/수정 어드민 추가
jun108059 Dec 10, 2025
1ed3d25
✨ 웹캠 조회/수정 어드민 추가
jun108059 Dec 10, 2025
dd2132f
Merge pull request #7 from jun108059/feature/admin-webcam-update
jun108059 Dec 10, 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
34 changes: 12 additions & 22 deletions .github/workflows/deploy-dev.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Deploy to Development

on:
push:
branches:
- develop
workflow_dispatch:
Copy link

Copilot AI Sep 21, 2025

Choose a reason for hiding this comment

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

The workflow_dispatch trigger should include input parameters or documentation explaining when and how this manual deployment should be used, especially since it replaced automatic develop branch deployment.

Copilot uses AI. Check for mistakes.

concurrency:
group: deploy-weski-${{ github.ref }}
cancel-in-progress: true

jobs:
deploy-dev:
runs-on: ubuntu-latest

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

- name: Set up JDK 17
Expand All @@ -19,28 +21,16 @@ jobs:
java-version: '17'
distribution: 'temurin'

- name: Cache Gradle dependencies
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: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Setup Gradle (cache)
uses: gradle/actions/setup-gradle@v4

- name: Run tests
run: ./gradlew test

- name: Build application
run: ./gradlew clean build -x test
- name: Install Railway CLI
run: npm install -g @railway/cli

- name: Deploy to Railway Dev
uses: railwayapp/cli@v3
with:
command: up --service WeSki-server-dev
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN }}
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_DEV }}
run: railway up --detach --service ${{ secrets.RAILWAY_SERVICE_ID_DEV }}
52 changes: 52 additions & 0 deletions .github/workflows/deploy-frontend-dev.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Deploy Frontend to Development

on:
workflow_dispatch:

concurrency:
group: deploy-frontend-dev-${{ github.ref }}
cancel-in-progress: true

jobs:
deploy-frontend-dev:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: 'weski-admin/package-lock.json'

- name: Install dependencies
working-directory: weski-admin
run: npm ci

- name: Type check
working-directory: weski-admin
run: npm run type-check

- name: Lint
working-directory: weski-admin
run: npm run lint

- name: Build application
working-directory: weski-admin
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.BACKEND_URL_DEV }}
RAILWAY_ENVIRONMENT: development
NODE_ENV: production

- name: Install Railway CLI
run: npm install -g @railway/cli

- name: Deploy to Railway Dev
working-directory: weski-admin
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_DEV }}
run: railway up --detach --service ${{ secrets.RAILWAY_SERVICE_ID_FRONTEND_DEV }}
52 changes: 52 additions & 0 deletions .github/workflows/deploy-frontend-prod.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
name: Deploy Frontend to Production

on:
workflow_dispatch:

concurrency:
group: deploy-frontend-prod-${{ github.ref }}
cancel-in-progress: true

jobs:
deploy-frontend-prod:
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
cache: 'npm'
cache-dependency-path: 'weski-admin/package-lock.json'

- name: Install dependencies
working-directory: weski-admin
run: npm ci

- name: Type check
working-directory: weski-admin
run: npm run type-check

- name: Lint
working-directory: weski-admin
run: npm run lint

- name: Build application
working-directory: weski-admin
run: npm run build
env:
NEXT_PUBLIC_API_URL: ${{ secrets.BACKEND_URL_PROD }}
RAILWAY_ENVIRONMENT: production
NODE_ENV: production

- name: Install Railway CLI
run: npm install -g @railway/cli

- name: Deploy to Railway Prod
working-directory: weski-admin
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_PROD }}
run: railway up --detach --service ${{ secrets.RAILWAY_SERVICE_ID_FRONTEND_PROD }}
34 changes: 12 additions & 22 deletions .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
name: Deploy to Production

on:
push:
branches:
- main
workflow_dispatch:
Copy link

Copilot AI Sep 21, 2025

Choose a reason for hiding this comment

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

The workflow_dispatch trigger should include input parameters or documentation explaining when and how this manual deployment should be used, especially since it replaced automatic main branch deployment.

Copilot uses AI. Check for mistakes.

concurrency:
group: deploy-weski-${{ github.ref }}
cancel-in-progress: true

jobs:
deploy-prod:
runs-on: ubuntu-latest

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

- name: Set up JDK 17
Expand All @@ -19,28 +21,16 @@ jobs:
java-version: '17'
distribution: 'temurin'

- name: Cache Gradle dependencies
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: Grant execute permission for gradlew
run: chmod +x gradlew
- name: Setup Gradle (cache)
uses: gradle/actions/setup-gradle@v4

- name: Run tests
run: ./gradlew test

- name: Build application
run: ./gradlew clean build -x test
- name: Install Railway CLI
run: npm install -g @railway/cli

- name: Deploy to Railway Prod
uses: railwayapp/cli@v3
with:
command: up --service WeSki-server
env:
RAILWAY_TOKEN: ${{ secrets.RAILWAY_PROD_TOKEN }}
RAILWAY_TOKEN: ${{ secrets.RAILWAY_TOKEN_PROD }}
run: railway up --detach --service ${{ secrets.RAILWAY_SERVICE_ID_PROD }}
39 changes: 28 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,15 +1,32 @@
FROM openjdk:17-jdk-slim
## ===== Build stage =====
FROM gradle:8.9-jdk17-alpine AS build
WORKDIR /workspace

# tzdata 패키지 설치 및 시간대 설정
RUN apt-get update && apt-get install -y tzdata && \
ln -sf /usr/share/zoneinfo/Asia/Seoul /etc/localtime && \
echo "Asia/Seoul" > /etc/timezone
# Gradle 캐시 최적화를 위해 래퍼/설정 먼저 복사
COPY gradle gradle
COPY gradlew settings.gradle.kts build.gradle.kts ./
RUN chmod +x gradlew

EXPOSE 8080
# 소스 복사 후 빌드 (테스트는 CI에서 돌리므로 -x test)
COPY src ./src
RUN ./gradlew --no-daemon clean bootJar -x test

## ===== Runtime stage =====
FROM eclipse-temurin:17-jre-alpine

RUN addgroup -S spring && adduser -S spring -G spring
USER spring

ARG JAR_FILE=build/libs/*.jar
COPY ${JAR_FILE} weski-app.jar
WORKDIR /app
COPY --from=build /workspace/build/libs/*.jar /app/app.jar

# 런타임 환경
ENV TZ=Asia/Seoul \
JAVA_OPTS="-XX:MaxRAMPercentage=75.0 -XX:+UseG1GC" \
SPRING_PROFILES_ACTIVE=dev

# 실제 개방 포트는 Railway가 PORT로 지정할 수 있음
EXPOSE 8080

CMD java -Dserver.port=${PORT:-8080} \
-Dspring.profiles.active=${SPRING_PROFILES_ACTIVE:-prod} \
-jar /weski-app.jar
# 별도 스크립트 없이 바로 실행
ENTRYPOINT [ "sh", "-c", "exec java $JAVA_OPTS -jar /app/app.jar" ]
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class AdminSkiResortController(
): ResponseEntity<ApiResponse<Unit>> {
adminSkiResortService.deleteSkiResort(resortId)
return ResponseEntity.ok(
ApiResponse.success<Unit>("스키장이 성공적으로 삭제되었습니다"),
ApiResponse.success("스키장이 성공적으로 삭제되었습니다"),
)
}

Expand All @@ -85,7 +85,7 @@ class AdminSkiResortController(
fun updateAllResortStatus(): ResponseEntity<ApiResponse<Unit>> {
adminSkiResortService.updateAllResortStatus()
return ResponseEntity.ok(
ApiResponse.success<Unit>("모든 스키장 상태가 성공적으로 업데이트되었습니다"),
ApiResponse.success("모든 스키장 상태가 성공적으로 업데이트되었습니다"),
)
}

Expand All @@ -94,7 +94,7 @@ class AdminSkiResortController(
fun updateAllSlopeCount(): ResponseEntity<ApiResponse<Unit>> {
adminSkiResortService.updateAllSlopeCount()
return ResponseEntity.ok(
ApiResponse.success<Unit>("모든 스키장 슬로프 수가 성공적으로 업데이트되었습니다"),
ApiResponse.success("모든 스키장 슬로프 수가 성공적으로 업데이트되었습니다"),
)
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package nexters.weski.ski.resort

import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository

@Repository
interface SkiResortRepository : JpaRepository<SkiResort, Long> {
fun findAllByOrderByOpeningDateAsc(): List<SkiResort>

Expand Down
79 changes: 42 additions & 37 deletions src/test/kotlin/nexters/weski/ski/resort/SkiResortControllerTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,44 +15,49 @@ import org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath
import org.springframework.test.web.servlet.result.MockMvcResultMatchers.status

@WebMvcTest(SkiResortController::class)
@ComponentScan(
excludeFilters = [ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = [JpaConfig::class]
)]
@WebMvcTest(
controllers = [SkiResortController::class],
excludeFilters = [
ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
classes = [AdminSkiResortController::class, JpaConfig::class],
),
],
)
class SkiResortControllerTest @Autowired constructor(
private val mockMvc: MockMvc
) {
class SkiResortControllerTest
@Autowired
constructor(
private val mockMvc: MockMvc,
) {
@MockkBean
lateinit var skiResortService: SkiResortService

@MockkBean
lateinit var skiResortService: SkiResortService
@Test
fun `GET api_ski-resorts should return list of ski resorts`() {
val currentWeather = SimpleCurrentWeatherDto(-1, "맑음")
val weeklyWeather =
listOf(
WeeklyWeatherDto("월", 5, -3, "맑음"),
WeeklyWeatherDto("화", 6, -2, "맑음"),
WeeklyWeatherDto("수", 7, -1, "맑음"),
WeeklyWeatherDto("목", 8, 0, "맑음"),
WeeklyWeatherDto("금", 9, 1, "맑음"),
WeeklyWeatherDto("토", 10, 2, "맑음"),
WeeklyWeatherDto("일", 11, 3, "맑음"),
)
// Given
val skiResorts =
listOf(
SkiResortResponseDto(1, "스키장 A", ResortStatus.운영중.name, "미정", "미정", 3, currentWeather, weeklyWeather),
SkiResortResponseDto(2, "스키장 B", ResortStatus.운영중.name, "미정", "미정", 4, currentWeather, weeklyWeather),
)
every { skiResortService.getAllSkiResortsAndWeather() } returns skiResorts

@Test
fun `GET api_ski-resorts should return list of ski resorts`() {
val currentWeather = SimpleCurrentWeatherDto(-1, "맑음")
val weeklyWeather = listOf(
WeeklyWeatherDto("월", 5, -3, "맑음"),
WeeklyWeatherDto("화", 6, -2, "맑음"),
WeeklyWeatherDto("수", 7, -1, "맑음"),
WeeklyWeatherDto("목", 8, 0, "맑음"),
WeeklyWeatherDto("금", 9, 1, "맑음"),
WeeklyWeatherDto("토", 10, 2, "맑음"),
WeeklyWeatherDto("일", 11, 3, "맑음")
)
// Given
val skiResorts = listOf(
SkiResortResponseDto(1, "스키장 A", ResortStatus.운영중.name, "미정", "미정", 3, currentWeather, weeklyWeather),
SkiResortResponseDto(2, "스키장 B", ResortStatus.운영중.name, "미정", "미정", 4, currentWeather, weeklyWeather),
)
every { skiResortService.getAllSkiResortsAndWeather() } returns skiResorts


// When & Then
mockMvc.perform(get("/api/ski-resorts"))
.andExpect(status().isOk)
.andExpect(jsonPath("$[0].name").value("스키장 A"))
.andExpect(jsonPath("$[1].name").value("스키장 B"))
// When & Then
mockMvc
.perform(get("/api/ski-resorts"))
.andExpect(status().isOk)
.andExpect(jsonPath("$[0].name").value("스키장 A"))
.andExpect(jsonPath("$[1].name").value("스키장 B"))
}
}
}
Loading