본문 바로가기
토이프로젝트/선착순쿠폰발급

[선착순 쿠폰 발급 3편] API 설명

by 무대포 개발자 2023. 3. 27.
728x90
반응형

source 는 Github 에 있습니다.

목차는 선착순 쿠폰 발급 에 있습니다.

[선착순 쿠폰 발급 3편] API 설명

코틀린, Redis 공부를 위해 작성했습니다.

API 설명

  • 쿠폰 발급 요청을 해주는 API 에 대해 간단히 정리했습니다.

Controller Source

@RestController
class CouponIssuanceController(
    val couponIssuanceService: CouponIssuanceService
) {
    @PostMapping("api/v1/coupon/issuance/request")
    fun requestCouponIssuance(
        @RequestBody request: CouponIssuanceDto.CouponIssuanceRequest
    ): ApiResponse<CouponIssuanceDto.CouponIssuanceResponse> {
        val couponIssuanceDtoMapper = Mappers.getMapper(CouponIssuanceDtoMapper::class.java)
        var couponIssuanceRequest = couponIssuanceDtoMapper.convertToCouponIssuanceRequest(request);
        var result = couponIssuanceService.requestPreGeneratedCouponIssuance(couponIssuanceRequest);
        return ApiResponse.success(couponIssuanceDtoMapper.convertCouponIssuanceResponse(result))
    }
}
  • 요약하면 /api/v1/coupon/issuance/request POST 요청이 들어왔을 때, request 를 변환해서 couponIssuanceService.requestPreGeneratedCouponIssuance 를 호출합니다.
  • 응답 값은 couponId 입니다.

Redis 호출 Source

@Repository
class CouponRedisRepository(
    private val redisTemplate: RedisTemplate<String, Any>
) {
    private val AVAILABLE_COUPON_REDIS_KEY: String = "available-coupon"
    private val COUPON_USER_MAPPING_REDIS_KEY: String = "coupon-user-mapping"

    private val requestPreGeneratedCouponIssuanceScript: RedisScript<String> = DefaultRedisScript(
        """
            -- 체크할 user Id를 매개변수로 받음
            local userId = ARGV[1]

            -- Hash에서 userId가 있는지 확인하고, 있다면 "exist"를 리턴
            local check = redis.call('HEXISTS', KEYS[2], ARGV[1])
            if check == 1 then
                return "exist"
            end

            -- Set에서 쿠폰 ID를 가져와서 제거
            local couponId = redis.call('SPOP', KEYS[1])

            if not couponId or couponId == nil then
                return "empty"
            end

            -- Hash에 쿠폰 ID를 저장
            redis.call('HSET', KEYS[2], ARGV[1], couponId)

            -- 발급된 쿠폰 ID 리턴
            return couponId
        """,
        String::class.java
    )

    /**
     * HASH coupon-user-mapping --> userId couponId 맵핑
     * SADD available-coupon
     */
    fun requestPreGeneratedCouponIssuance(userId: String): String {
        val result: String? = redisTemplate.execute(
            requestPreGeneratedCouponIssuanceScript,
            listOf(AVAILABLE_COUPON_REDIS_KEY, COUPON_USER_MAPPING_REDIS_KEY),  // KEYS[1~2]
            userId // ARGS[1]
        )
        return Optional.ofNullable(result).orElse(Strings.EMPTY)
    }
}
  • 요약하면 controller --> service --> redis 를 호출하는 형태이며, requestPreGeneratedCouponIssuance 가 실행이 됩니다.

  • requestPreGeneratedCouponIssuance 가 하는 역할은 redis 의 lua script 를 실행시킵니다.

  • Lua script 내용은 다음과 같습니다.

    1. 처음에 중복 사용자인지 체크한 후 (HEXISTS), 쿠폰이 저장돼있는 redis key 에 접근해서 couponId 를 가져옵니다. (만약 couponId 가 없으면 empty 를 리턴합니다.)
    2. couponId 를 couponId, userId 맵핑 HASH 에 add 합니다.

댓글