Skip to content
Merged
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
120 changes: 60 additions & 60 deletions .github/workflows/cd-dev.yml
Original file line number Diff line number Diff line change
@@ -1,76 +1,76 @@
name: dev 서버 CD 실행
# name: dev 서버 CD 실행

on:
push:
branches: [ "develop" ]
# on:
# push:
# branches: [ "develop" ]

permissions:
contents: read
# permissions:
# contents: read

jobs:
deploy:
runs-on: ubuntu-latest
steps:
# jobs:
# deploy:
# runs-on: ubuntu-latest
# steps:

# repository checkout
- name: Checkout
uses: actions/checkout@v4
# # repository checkout
# - name: Checkout
# uses: actions/checkout@v4

# JDK 환경 설치
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: '17'
distribution: 'corretto'
# # JDK 환경 설치
# - name: Set up JDK 17
# uses: actions/setup-java@v4
# with:
# java-version: '17'
# distribution: 'corretto'

# 환경 변수 설정
- name: application.yml 파일 설정
run: |
mkdir -p src/main/resources
echo "${{ secrets.APPLICATION_YML_DEV }}" > ./src/main/resources/application.yml
# # 환경 변수 설정
# - name: application.yml 파일 설정
# run: |
# mkdir -p src/main/resources
# echo "${{ secrets.APPLICATION_YML_DEV }}" > ./src/main/resources/application.yml

# Gradle 환경 설치
- name: Setup Gradle
uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0
# # Gradle 환경 설치
# - name: Setup Gradle
# uses: gradle/actions/setup-gradle@af1da67850ed9a4cedd57bfd976089dd991e2582 # v4.0.0

# Gradle 권한 변경
- name: Grant execute permission for gradlew
run: chmod +x gradlew
# # Gradle 권한 변경
# - name: Grant execute permission for gradlew
# run: chmod +x gradlew

# Build 진행
- name: Build with Gradle
run: ./gradlew clean build --stacktrace
shell: bash
# # Build 진행
# - name: Build with Gradle
# run: ./gradlew clean build --stacktrace
# shell: bash

# docker build & push
- name: Docker build & push
run: |
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
docker build -t ${{ secrets.DOCKER_USERNAME }}/uble-dev -f ./Dockerfile .
docker push ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest
# # docker build & push
# - name: Docker build & push
# run: |
# docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
# docker build -t ${{ secrets.DOCKER_USERNAME }}/uble-dev -f ./Dockerfile .
# docker push ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest

# 배포
- name: EC2 Connect & Deploy
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.SERVER_IP_DEV }}
username: ${{ secrets.SSH_USER }}
key: ${{ secrets.SSH_PRIVATE_KEY }}
script_stop: true
script: |
# 모든 컨테이너 중지 및 제거 (컨테이너가 있는 경우)
sudo docker stop $(docker ps -a -q) || true
sudo docker rm -fv $(docker ps -aq) || true
# # 배포
# - name: EC2 Connect & Deploy
# uses: appleboy/ssh-action@master
# with:
# host: ${{ secrets.SERVER_IP_DEV }}
# username: ${{ secrets.SSH_USER }}
# key: ${{ secrets.SSH_PRIVATE_KEY }}
# script_stop: true
# script: |
# # 모든 컨테이너 중지 및 제거 (컨테이너가 있는 경우)
# sudo docker stop $(docker ps -a -q) || true
# sudo docker rm -fv $(docker ps -aq) || true

# 이전 이미지 삭제
sudo docker image rm ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest || true
# # 이전 이미지 삭제
# sudo docker image rm ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest || true

# 최신 Docker 이미지 pull 받기
sudo docker pull ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest
# # 최신 Docker 이미지 pull 받기
# sudo docker pull ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest

# Docker 컨테이너 실행
sudo docker run -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest
# # Docker 컨테이너 실행
# sudo docker run -d -p 8080:8080 ${{ secrets.DOCKER_USERNAME }}/uble-dev:latest

# 불필요한 Docker 이미지 정리
sudo docker image prune -f
# # 불필요한 Docker 이미지 정리
# sudo docker image prune -f

57 changes: 40 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
# UBLE BACK-END
# UBLE BACK-END REPOSITORY

## 프로젝트 소개
- 프로젝트명 : Uble
- 프로젝트 주제 : LG U+ 멤버십 제휴처 안내 지도 서비스
- 프로젝트 기간 : 2025.06.30 - 2025.08.08

<div align="center">
<img src="https://github.com/user-attachments/assets/3c016595-0424-404d-bfdb-c8068c1e1e37" alt="탐험하는 귀여운 마스코트" width="600" />
</div>

**🌐 서비스 바로가기** : https://www.u-ble.com

**📊 관리자 페이지 바로가기** : https://admin.u-ble.com

**💡 UBLE 이란?** : https://www.u-ble.com/intro


<br>

- **프로젝트명** : UBLE
- **프로젝트 주제** : LG U+ 멤버십 제휴처 안내 지도 서비스
- **프로젝트 기간** : 2025.06.30 - 2025.08.08


## 🧑🏻‍💻 팀원 소개
<div align="center">
Expand Down Expand Up @@ -63,12 +78,13 @@
<br><br><br>

## 💾 DB
### 1. RDB
<img width="1964" height="1742" alt="ERD_BASIC 1" src="https://github.com/user-attachments/assets/3e7179f7-e7d7-47f2-a7ac-0183c117d7f3" />
📸 ERD : https://www.erdcloud.com/d/C9vcCB4kCHXRWWczR
### 1. PostgreSQL
<img width="2018" height="1799" alt="Uble ERD (2)" src="https://github.com/user-attachments/assets/bbdd5306-1e79-46ac-9e53-1125e1546ba1" />

<br><br>

### 2. ES
### 2. Elasticsearch
**1) 검색용 INDEX**

<img width="2062" height="1027" alt="ERD_SEARCH 1" src="https://github.com/user-attachments/assets/9fbde427-7051-4e91-ba2c-d657994315e4" />
Expand All @@ -82,7 +98,8 @@
<br><br><br>

## 🛠️ 시스템 아키텍처
<img width="10327" height="6106" alt="Group 427320386" src="https://github.com/user-attachments/assets/4ab6df8a-7bd0-4de8-a576-0638e27856b2" />
<img width="9888" height="6041" alt="Group 427320387" src="https://github.com/user-attachments/assets/80a2e554-64f7-4b73-bb20-37dbc553990b" />


---

Expand Down Expand Up @@ -147,6 +164,18 @@

<br><br><br>

## 📚 스터디

<ul>
<li><a href="https://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-%EC%9D%B4%EC%9A%A9-%EB%82%B4%EC%97%AD-%EC%B4%88%EA%B8%B0%ED%99%94%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC-%EC%A0%81%EC%9A%A9%EA%B8%B0"> 📆 이용 내역 초기화를 위한 스케줄러 적용기</a></li>
<li><a href="http://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-%EC%A0%9C%ED%9C%B4%EC%B2%98-%EC%B6%94%EC%B2%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B0%9C%EB%B0%9C"> ⛓️ 제휴처 추천 알고리즘 개발 </a></li>
<li><a href="https://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-Redis-%EC%BA%90%EC%8B%B1%EC%9D%84-%ED%86%B5%ED%95%9C-%EC%B6%94%EC%B2%9C-%EA%B2%B0%EA%B3%BC-%EC%9D%91%EB%8B%B5-%EC%86%8D%EB%8F%84-%EA%B0%9C%EC%84%A0"> ⛓️ Redis 캐싱을 통한 추천 결과 응답 속도 개선 </a></li>
<li><a href="https://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-%EB%A7%A4%EC%9E%A5-%ED%83%90%EC%83%89-%EA%B8%B0%EB%8A%A5-%EC%84%B1%EB%8A%A5-%EA%B0%9C%EC%84%A0"> 🗺️ 매장 탐색 기능 성능 개선 </a></li>
<li><a href="https://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-Elasticsearch%EB%A5%BC-%ED%99%9C%EC%9A%A9%ED%95%9C-%EA%B2%80%EC%83%89-%EA%B8%B0%EB%8A%A5-%EA%B5%AC%ED%98%84"> 🔎 Elasticsearch를 활용한 검색 기능 구현 </a></li>
</ul>

<br><br><br>

## ⚖️ 컨벤션

### **[ PACKAGE STRUCTURE ]**
Expand All @@ -168,6 +197,7 @@
├─ 📁 common
├─ 📁 feedback
├─ 📁 pin
├─ 📁 search
├─ 📁 store
└─ 📁 users

Expand Down Expand Up @@ -282,14 +312,7 @@ Ex. [UBLE-12] fix: 즐겨찾기 DTO 수정
| 스토어 | `5000` |
| 북마크 | `6000` |
| 피드백 | `7000` |

<br><br><br>

## [📚 Github Wiki]()
**STUDY**
<ul>
<li><a href="https://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-%EC%9D%B4%EC%9A%A9-%EB%82%B4%EC%97%AD-%EC%B4%88%EA%B8%B0%ED%99%94%EB%A5%BC-%EC%9C%84%ED%95%9C-%EC%8A%A4%EC%BC%80%EC%A4%84%EB%9F%AC-%EC%A0%81%EC%9A%A9%EA%B8%B0"> 📆 이용 내역 초기화를 위한 스케줄러 적용기</a></li>
<li><a href="http://github.com/MapChillE/uble-be/wiki/%5BSTUDY%5D-%EC%A0%9C%ED%9C%B4%EC%B2%98-%EC%B6%94%EC%B2%9C-%EC%95%8C%EA%B3%A0%EB%A6%AC%EC%A6%98-%EA%B0%9C%EB%B0%9C"> ⛓️ 제휴처 추천 알고리즘 개발 </a></li>
</ul>
| 핀 | `8000` |
| 공통 | `9000` |

---
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import com.ureca.uble.domain.users.repository.UserRepository;
import com.ureca.uble.entity.Token;
import com.ureca.uble.entity.User;
import com.ureca.uble.entity.enums.Role;
import com.ureca.uble.global.exception.GlobalException;
import com.ureca.uble.global.security.jwt.JwtProvider;
import com.ureca.uble.global.security.jwt.JwtValidator;
Expand Down Expand Up @@ -70,6 +71,10 @@ public User login(String code, HttpServletResponse response) {

jwtProvider.addAccessTokenHeader(response, accessToken);
jwtProvider.addRefreshTokenCookie(response, refreshToken);

if (user.getRole() == Role.TMP_USER){
jwtProvider.addTmpCheckCookie(response);
}
jwtProvider.addAuthCheckCookie(response);

return user;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.ureca.uble.domain.users.service.UserService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
Expand All @@ -30,11 +31,12 @@ public CommonResponse<GetUserInfoRes> getUserInfo(
@Operation(summary = "사용자 정보 최초 입력 & 수정", description = "사용자 정보를 수정합니다.")
@PutMapping("/userInfo")
public CommonResponse<UpdateUserInfoRes>updateUserInfo(
HttpServletResponse response,
@Parameter(description = "사용자정보", required = true)
@AuthenticationPrincipal Long userId,
@Valid @RequestBody UpdateUserInfoReq request
){
return CommonResponse.success(userService.updateUserInfo(userId, request));
return CommonResponse.success(userService.updateUserInfo(response, userId, request));
}

@Operation(summary = "제휴처 매장 추천 정보 조회", description = "사용자에게 맞는 제휴처 매장 추천 정보를 조회합니다.")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
import com.ureca.uble.entity.UserCategory;
import com.ureca.uble.entity.document.UsageHistoryDocument;
import com.ureca.uble.global.exception.GlobalException;
import com.ureca.uble.global.security.jwt.JwtProvider;

import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.slf4j.MDC;
import org.springframework.data.elasticsearch.client.elc.ElasticsearchAggregations;
Expand Down Expand Up @@ -43,6 +46,7 @@ public class UserService {
private final UsageHistoryDocumentRepository usageHistoryDocumentRepository;
private final BrandRepository brandRepository;
private final BookmarkRepository bookmarkRepository;
private final JwtProvider jwtProvider;

/**
* 사용자 정보 조회
Expand All @@ -62,7 +66,7 @@ public GetUserInfoRes getUserInfo(Long userId) {
* 사용자 정보 갱신
*/
@Transactional
public UpdateUserInfoRes updateUserInfo(Long userId, UpdateUserInfoReq request) {
public UpdateUserInfoRes updateUserInfo(HttpServletResponse response, Long userId, UpdateUserInfoReq request) {
User user = findUser(userId);

user.updateUserInfo(
Expand All @@ -80,6 +84,8 @@ public UpdateUserInfoRes updateUserInfo(Long userId, UpdateUserInfoReq request)
userCategoryRepository.save(userCategory);
});

jwtProvider.deleteTmpCheckCookie(response);

return UpdateUserInfoRes.of(user, request.getCategoryIds());
}

Expand Down
28 changes: 28 additions & 0 deletions src/main/java/com/ureca/uble/global/security/jwt/JwtProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,19 @@ public void addAuthCheckCookie(HttpServletResponse response) {
response.addHeader("Set-Cookie", cookie.toString());
}

public void addTmpCheckCookie(HttpServletResponse response) {
ResponseCookie cookie = ResponseCookie.from("TmpCheck", "true")
.path("/")
.httpOnly(false)
.secure(isSecure)
.maxAge(REFRESH_TOKEN_VALIDITY_MILLIS / 1000)
.sameSite(sameSite)
.domain(cookieDomain.isBlank() ? null : cookieDomain)
.build();

response.addHeader("Set-Cookie", cookie.toString());
}

public void deleteRefreshTokenCookie(HttpServletResponse response){
ResponseCookie cookie = ResponseCookie.from("refreshToken", "")
.path("/")
Expand All @@ -114,6 +127,21 @@ public void deleteAuthCheckCookie(HttpServletResponse response){
response.addHeader("Set-Cookie", cookie.toString());
}

public void deleteTmpCheckCookie(HttpServletResponse response){
ResponseCookie cookie = ResponseCookie.from("TmpCheck", "")
.path("/")
.httpOnly(false)
.secure(isSecure)
.maxAge(0)
.sameSite(sameSite)
.domain(cookieDomain.isBlank() ? null : cookieDomain)
.build();

response.addHeader("Set-Cookie", cookie.toString());
}



public LocalDateTime getRefreshTokenExpiry(String token){
Claims claims = Jwts.parser()
.verifyWith(Keys.hmacShaKeyFor(secret.getBytes()))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
import com.ureca.uble.entity.enums.Gender;
import com.ureca.uble.entity.enums.Rank;
import com.ureca.uble.global.exception.GlobalException;
import com.ureca.uble.global.security.jwt.JwtProvider;

import jakarta.servlet.http.HttpServletResponse;

@ExtendWith(MockitoExtension.class)
public class UserServiceTest {
Expand All @@ -44,6 +47,12 @@ public class UserServiceTest {
@InjectMocks
private UserService userService;

@Mock
private HttpServletResponse response;

@Mock
private JwtProvider jwtProvider;

@Test
@DisplayName("사용자 ID로 사용자 정보를 조회한다.")
void getUserInfoSuccess(){
Expand Down Expand Up @@ -105,9 +114,10 @@ void updateUserInfoSuccess(){
when(categoryRepository.findAllById(categoryIds)).thenReturn(List.of(cat1, cat2));

//when
UpdateUserInfoRes result = userService.updateUserInfo(userId, request);
UpdateUserInfoRes result = userService.updateUserInfo(response, userId, request);

//then
verify(jwtProvider).deleteTmpCheckCookie(response);
verify(user).updateUserInfo(Rank.VIP, Gender.FEMALE, LocalDate.of(1999, 1, 1), "123456787654321");
verify(userCategoryRepository).deleteByUser(user);
verify(userCategoryRepository, times(2)).save(any(UserCategory.class));
Expand All @@ -126,7 +136,7 @@ void updateUserInfo_userNotFound(){

//when, then
GlobalException ex = assertThrows(GlobalException.class, () ->
userService.updateUserInfo(123L, req)
userService.updateUserInfo(response,123L, req)
);
assertThat(ex.getResultCode()).isEqualTo(UserErrorCode.USER_NOT_FOUND);
}
Expand Down
Loading