Skip to content

Commit

Permalink
feat: commit
Browse files Browse the repository at this point in the history
  • Loading branch information
hjj4060 committed Sep 23, 2024
2 parents 5e49ce0 + c38db16 commit feee25c
Show file tree
Hide file tree
Showing 45 changed files with 1,794 additions and 110 deletions.
12 changes: 4 additions & 8 deletions .github/workflows/dev-aws-CI-CD.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ jobs:
run: chmod +x gradlew

- name: Build With Gradle
env:
JASYPT_ENCRYPTOR_PASSWORD: ${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }}
run: ./gradlew build -x test --info

- name: List All Files for Debugging
Expand Down Expand Up @@ -75,16 +73,15 @@ jobs:
- name: Show docker-compose.yml Content
run: cat docker-compose.yml # docker-compose.yml 파일 내용 확인

# Send nginx.conf to a temporary location
- name: Send nginx.conf to Home Directory
- name: Send docker-compose.yml and nginx.conf to Home Directory
uses: appleboy/scp-action@master
with:
username: ubuntu
host: ${{ secrets.AWS_DEV_HOSTNAME }}
key: ${{ secrets.AWS_DEV_PRIVATE_KEY }}
source: "./nginx/conf.d/nginx.conf"
target: "/home/ubuntu" # 정확한 파일 경로 지정
strip_components: 3 # 경로 구성 요소를 제거하여 파일만 전송
source: "./docker-compose.yml,./nginx/conf.d/nginx.conf"
target: "/home/ubuntu/"
strip_components: 3 # nginx.conf 경로 구성 요소를 제거하여 파일만 전송

# Move nginx.conf from Home Directory to /etc/nginx/conf.d
- name: Move nginx.conf to /etc/nginx/conf.d
Expand Down Expand Up @@ -143,7 +140,6 @@ jobs:
docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}
export DOCKER_REPOSITORY=${{ secrets.DOCKER_REPOSITORY }}
export DOCKER_REPOSITORY_NGINX=${{ secrets.DOCKER_REPOSITORY_NGINX }}
export JASYPT_ENCRYPTOR_PASSWORD=${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }}
# Pull 최신 이미지
docker-compose -f /home/ubuntu/docker-compose.yml pull
# 새 컨테이너 실행
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/gradle.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ jobs:
run: |
./gradlew build
./gradlew --info test
env:
JASYPT_ENCRYPTOR_PASSWORD: ${{ secrets.JASYPT_ENCRYPTOR_PASSWORD }}
- name: Publish Unit Test Results
uses: EnricoMi/publish-unit-test-result-action@v1
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,6 @@
## 프로젝트 기간

이 프로젝트의 기간은 2024년 7월 23일부터 2024년 8월 30일까지입니다.

## CI/CD Architecture
![image](https://github.com/user-attachments/assets/8dc985e5-68b8-4982-897b-c8e60d8b1b6f)
9 changes: 9 additions & 0 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ dependencies {

//web
implementation("org.springframework.boot:spring-boot-starter-web")
implementation("org.springframework.boot:spring-boot-starter-webflux")

//thymeleaf
implementation("org.springframework.boot:spring-boot-starter-thymeleaf")

// logging
Expand Down Expand Up @@ -69,6 +72,12 @@ dependencies {
testImplementation("io.kotest:kotest-runner-junit5:${kotestVersion}")
testImplementation("org.springframework.boot:spring-boot-starter-test")
testImplementation("org.jetbrains.kotlin:kotlin-test-junit5")
testImplementation("com.squareup.okhttp3:okhttp:4.9.1")
testImplementation("com.squareup.okhttp3:mockwebserver:4.9.1")

//oauth2
implementation ("org.springframework.boot:spring-boot-starter-oauth2-client")
implementation ("org.springframework.security:spring-security-oauth2-jose")

//etc
implementation("org.springdoc:springdoc-openapi-starter-webmvc-ui:2.2.0") //swagger
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,15 @@ class SecurityConfiguration(
"/v1/orders/list", "/ordersPagingOffset", "/ordersPagingSlice", "/v2/orders/list",
"/v1/seller/orders/**", "/v1/seller/orders", "/v1/orders/**", "/v1/orderdetails/**",
"/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**",
"/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO",
"/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png",
"/swagger-resources/**", "/swagger-ui/**", "/v3/api-docs/**", "/v1/members/auth/**",
"config-service/**",
"/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO",
"/auth/**", "/kakaoLogin/**", "/kakao_login_medium_narrow.png", "/v1/members?signUpType=KAKAO",
"/v1/members?signUpType=KAKAO",
"/v1/members/**",
"/v1/members/auth/**",
"config-service/**", "products/**", "productDetail", "payment/**", "/v1/payments/**"
).permitAll()
.requestMatchers(HttpMethod.POST, "/v1/members").permitAll()
Expand Down
20 changes: 20 additions & 0 deletions src/main/kotlin/org/store/clothstar/common/config/WebConfig.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package org.store.clothstar.common.config

import org.springframework.context.annotation.Configuration
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer

@Configuration
class WebConfig : WebMvcConfigurer {

override fun addResourceHandlers(registry: ResourceHandlerRegistry) {
registry.addResourceHandler("/favicon.ico")
.addResourceLocations("classpath:/static/")
registry.addResourceHandler("/js/**")
.addResourceLocations("classpath:/static/js/")
registry.addResourceHandler("/css/**")
.addResourceLocations("classpath:/static/css/")
registry.addResourceHandler("/images/**")
.addResourceLocations("classpath:/static/images/")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ class GlobalExceptionHandler {
return ResponseEntity(errorResponseDTO, ex.errorCode.status)
}

// 회원가입 관련 에러처리
@ExceptionHandler(InvalidSignupMemberRequest::class)
fun handleInvalidSignupMemberRequest(ex: InvalidSignupMemberRequest): ResponseEntity<ErrorResponseDTO> {
val errorResponseDTO = ErrorResponseDTO(ex.errorCode.status.value(), ex.errorCode.message)
return ResponseEntity(errorResponseDTO, ex.errorCode.status)
}

@ExceptionHandler(InvalidSignupType::class)
fun handleInvalidSignupType(ex: InvalidSignupType): ResponseEntity<ErrorResponseDTO> {
val errorResponseDTO = ErrorResponseDTO(ex.errorCode.status.value(), ex.errorCode.message)
return ResponseEntity(errorResponseDTO, ex.errorCode.status)
}

// Order 관련 에러처리
@ExceptionHandler(OrderNotFoundException::class)
fun handleOrderNotFoundException(ex: OrderNotFoundException): ResponseEntity<ErrorResponseDTO> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package org.store.clothstar.common.dto

class SaveResponseDTO(
val id: Long,
val id: Long? = null,
val statusCode: Int,
val message: String,
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,17 @@ enum class ErrorCode(

INVALID_ROLE(HttpStatus.BAD_REQUEST, "계정 ID: %d - 이 계정은 요청된 권한(%s)을 가지고 있지 않습니다."),

// 회원가입 관련 에러코드
INVALID_SIGNUP_MEMBER_REQUEST(HttpStatus.BAD_REQUEST, "회원가입 시 회원 정보가 필요합니다."),
INVLIAD_SIGNUP_TYPE(HttpStatus.BAD_REQUEST, "지원하지 않는 회원가입 유형입니다."),

// Order 관련 에러코드
NOT_FOUND_ORDER(HttpStatus.NOT_FOUND, "존재하지 않는 주문번호입니다."),
INVALID_ORDER_STATUS_CONFIRMED(HttpStatus.BAD_REQUEST, "주문이 '입금확인' 상태가 아니므로 요청을 처리할 수 없습니다."),
INVALID_ORDER_STATUS_DELIVERED(HttpStatus.BAD_REQUEST, "주문이 '배송완료' 상태가 아니므로 요청을 처리할 수 없습니다."),
OUT_OF_STOCK(HttpStatus.BAD_REQUEST, "품절된 상품입니다."),
INSUFFICIENT_STOCK(HttpStatus.BAD_REQUEST, "주문 개수가 상품 재고보다 더 많아 요청을 처리할 수 없습니다.");


// Product 관련 에러코드


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.store.clothstar.common.error.exception

import org.store.clothstar.common.error.ErrorCode

class InvalidSignupMemberRequest(
val errorCode: ErrorCode
) : RuntimeException(errorCode.message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.store.clothstar.common.error.exception

import org.store.clothstar.common.error.ErrorCode

class InvalidSignupType(
val errorCode: ErrorCode
) : RuntimeException(errorCode.message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.store.clothstar.kakaoLogin.controller

import org.springframework.http.ResponseEntity
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import org.store.clothstar.kakaoLogin.service.KakaoLoginService

@RestController
class KakaoController(
private val kakaoLoginService: KakaoLoginService,
) {
// 인가코드 받기
@GetMapping("/auth/kakao/callback")
fun kakaoCallback(@RequestParam code: String): ResponseEntity<String> {
return ResponseEntity.ok(code)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.store.clothstar.kakaoLogin.controller

import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Controller
import org.springframework.ui.Model
import org.springframework.web.bind.annotation.GetMapping

@Controller
class KakaoLoginPageController {

@Value("\${spring.security.oauth2.client.registration.kakao.client_id}")
private lateinit var clientId: String

@Value("\${spring.security.oauth2.client.registration.kakao.redirect_uri}")
private lateinit var redirectUri: String

@GetMapping("/kakaoLogin/page")
fun loginPage(model: Model): String {
val location =
"https://kauth.kakao.com/oauth/authorize?response_type=code&client_id=$clientId&redirect_uri=$redirectUri"
model.addAttribute("location", location)

return "kakaoLogin"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.store.clothstar.kakaoLogin.dto

import com.fasterxml.jackson.annotation.JsonCreator
import com.fasterxml.jackson.annotation.JsonProperty

data class KakaoTokenResponseDto @JsonCreator constructor(

// 토큰 타입, bearer로 고정
@JsonProperty("token_type")
private val tokenType: String? = null,

// 사용자 액세스 토큰 값
@JsonProperty("access_token")
val accessToken: String? = null,

// 액세스 토큰과 ID 토큰의 만료 시간(초)
@JsonProperty("expires_in")
private val expiresIn: Int? = null,

// 사용자 리프레시 토큰 값
@JsonProperty("refresh_token")
private val refreshToken: String? = null,

// 리프레시 토큰 만료 시간(초)
@JsonProperty("refresh_token_expires_in")
private val refreshTokenExpiresIn: Int? = null,

// 인증된 사용자의 정보 조회 권한 범위 / 범위가 여러 개일 경우, 공백으로 구분
@JsonProperty("scope")
private val scope: String? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.store.clothstar.kakaoLogin.dto

import com.fasterxml.jackson.annotation.JsonProperty
import org.store.clothstar.kakaoLogin.vo.KakaoAccount
import org.store.clothstar.kakaoLogin.vo.Properties

class KakaoUserInfoResponseDto(
// 회원번호
@JsonProperty("id") val id: Long? = null,
// 서비스에 연결 완료된 시각, UTC*
@JsonProperty("connected_at") val connectedAt: String? = null,
// 카카오계정 정보
@JsonProperty("kakao_account") val kakaoAccount: KakaoAccount? = null,
// 사용자 프로퍼티(Property)
@JsonProperty("properties") val properties: Properties? = null,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
package org.store.clothstar.kakaoLogin.service

import com.fasterxml.jackson.databind.ObjectMapper
import io.github.oshai.kotlinlogging.KotlinLogging
import org.springframework.beans.factory.annotation.Value
import org.springframework.scheduling.annotation.EnableScheduling
import org.springframework.stereotype.Service
import org.springframework.util.LinkedMultiValueMap
import org.springframework.util.MultiValueMap
import org.springframework.web.reactive.function.BodyInserters
import org.springframework.web.reactive.function.client.WebClient
import org.store.clothstar.kakaoLogin.dto.KakaoTokenResponseDto
import org.store.clothstar.kakaoLogin.dto.KakaoUserInfoResponseDto

@EnableScheduling
@Service
class KakaoLoginService {

private val logger = KotlinLogging.logger {}

@Value("\${spring.security.oauth2.client.registration.kakao.client_id}")
private lateinit var clientId: String

@Value("\${spring.security.oauth2.client.registration.kakao.client_secret}")
private lateinit var clientSecret: String

@Value("\${spring.security.oauth2.client.registration.kakao.redirect_uri}")
private lateinit var redirectUri: String

@Value("\${spring.security.oauth2.client.provider.kakao.token_uri}")
lateinit var tokenUri: String

@Value("\${spring.security.oauth2.client.provider.kakao.user_info_uri}")
lateinit var userUri: String

// 토큰 가져오기
fun getAccessToken(code: String): KakaoTokenResponseDto {
// 토큰 요청 데이터
val params: MultiValueMap<String, String> = LinkedMultiValueMap()
params.add("code", code)
params.add("client_secret", clientSecret)
params.add("client_id", clientId)
params.add("grant_type", "authorization_code")
params.add("redirect_url", redirectUri)

logger.info { "Requesting token with params: $params" }

// 웹 클라이언트로 요청 보내기
val response = WebClient.create(tokenUri)
.post()
.body(BodyInserters.fromFormData(params))
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
.retrieve()
.bodyToMono(String::class.java)
.block()

logger.info { "Token response: $response" }

// json 응답을 객체로 변환
val objectMapper = ObjectMapper()
val kakaoToken: KakaoTokenResponseDto = objectMapper.readValue(response, KakaoTokenResponseDto::class.java)

logger.info { "Access Token : ${kakaoToken.accessToken}" }
return kakaoToken
}

// 사용자 정보 가져오기
fun getUserInfo(accessToken: String): KakaoUserInfoResponseDto {
// 웹 클라이언트로 요청 보내기
val response = WebClient.create(userUri)
.get()
.header("Authorization", "Bearer $accessToken")
.header("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
.retrieve()
.bodyToMono(String::class.java)
.block()

logger.info { "User info response: $response" }

// json 응답을 객체로 변환
val objectMapper = ObjectMapper()
val userInfo = objectMapper.readValue(response, KakaoUserInfoResponseDto::class.java)

logger.info { "email : ${userInfo.kakaoAccount!!.email}" }
return userInfo
}
}
Loading

0 comments on commit feee25c

Please sign in to comment.