๐Ÿ“• Spring Framework/Spring Project

[Refactor] ํŒจํ‚ค์ง€ ๊ตฌ์กฐ์™€ ์˜์กด์„ฑ

GroovyArea 2022. 10. 14. 22:57

๋‘ ๋ฒˆ์งธ ํ”„๋กœ์ ํŠธ์˜ ์ฝ”๋“œ ์ž‘์„ฑ์ด ๊ฑฐ์˜ ๋๋‚ฌ๊ณ , ํ…Œ์ŠคํŠธ ์ฝ”๋“œ ์ž‘์„ฑ์„ ์•ž๋‘๋ฉฐ ์ฝ”๋“œ ๋ฆฌ๋ทฐ๋ฅผ ๋ฐ›์•˜๋‹ค.

๊ฐ€์žฅ ํฐ ๊ณจ์ž๋Š” ์•„๋ฌด๋ž˜๋„ ์ฐธ์กฐ ๊ด€๊ณ„์ด๋‹ค.

 

ํŒจํ‚ค์ง€ ๊ตฌ์กฐ๋ฅผ Layered์—์„œ ์•ฝ๊ฐ„์˜ DDD(์• ๋งคํ•˜์ง€๋งŒ ใ…Žใ…Ž) ๋ฅผ ๊ณ๋“ค์ธ ๊ตฌ์กฐ๋กœ ๋ณ€๊ฒฝํ–ˆ๋‹ค. 

๊ทธ ๊ณผ์ •์—์„œ ํŒจํ‚ค์ง€ ๊ฐ„ ์˜์กด์„ฑ์— ๋Œ€ํ•ด์„œ ๊ณ ๋ฏผํ•ด๋ณด๊ณ  ์ž‘๋ช…ํ•˜๋Š” ๊ฒƒ๊ณผ ์„ค๊ณ„ํ•˜๋Š” ์‹œ๊ฐ„์ด ์ •๋ง ์˜ค๋ž˜ ๊ฑธ๋ ธ๋‹ค.

 

์ฝ”๋”ฉ์„ ๊ณต๋ถ€ํ•˜๋ฉด ํ•  ์ˆ˜๋ก ์ž‘์€ ๊ฒƒ์— ์‹œ๊ฐ„์„ ์˜ค๋ž˜ ๋“ค์ด๊ฒŒ ๋˜๋Š” ๊ฑธ ๋А๋‚€๋‹ค.

์–ด์ œ๋Š” ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ ์ง“๋Š”๋ฐ ๋ฐ˜๋‚˜์ ˆ์ด ๊ฑธ๋ ธ๋‹ค.

ํšŒ์‚ฌ์—์„œ๋Š” ๋ณ€์ˆ˜๋ช… ์ง“๋Š” ๊ฑธ๋กœ๋„ ํšŒ์˜๋ฅผ ํ•œ๋‹ค๊ณ  ํ•˜๋‹ˆ ์•ฝ๊ฐ„ ์‹ค๊ฐ์ด ๋‚˜๊ธฐ๋„ ํ•œ๋‹ค.

 

์ด๋ ‡๊ฒŒ ๋””ํ…Œ์ผํ•˜๊ฒŒ ์ฑ„์›Œ๋‚˜๊ฐ€๋ฉด ๊ทธ ๋งŒํผ ๋‚ด ์‹ค๋ ฅ์ด ๋œ๋‹ค๊ณ  ๋ฏฟ์Šต๋‹ˆ๋‹ค.

 

์ตœ์ƒ์œ„ ๊ตฌ์กฐ

  • auth : ์ธ์ฆ, ์ธ๊ฐ€ ์ฒ˜๋ฆฌ
    • ์Šคํ”„๋ง ์‹œํ๋ฆฌํ‹ฐ ์ด์šฉ
    • ์Šคํ”„๋ง ์ปจํ…Œ์ด๋„ˆ๊นŒ์ง€ ๋„๋‹ฌํ•˜์ง€ ์•Š๋Š” ํ•„ํ„ฐ ์œ„์ฃผ์ด๊ธฐ ๋•Œ๋ฌธ์— ํŒจํ‚ค์ง€๋ฅผ ๋”ฐ๋กœ ๊ตฌ์„ฑํ–ˆ๋‹ค.
  • domain 
    • ๋„๋ฉ”์ธ ๊ด€๋ จ ์„œ๋น„์Šค๋กœ ๊ตฌ์„ฑ๋œ ํŒจํ‚ค์ง€
  • global
    • ๋ชจ๋“  ํŒจํ‚ค์ง€์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅ
    • ์ „์—ญ์ ์œผ๋กœ ์ ์šฉ
  • usecase
    • ๋„๋ฉ”์ธ์˜ ๋น„์ฆˆ๋‹ˆ์Šค๋กœ์ง์—์„œ ํŒจํ‚ค์ง€ ๊ฐ„ ์˜์กด์„ฑ์„ ๋”ฐ๋กœ ๋ถ„๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ๊ตฌ์„ฑํ•œ ํŒจํ‚ค์ง€
    • ์ฃผ๋ฌธ, ๊ฒฐ์ œ์—์„œ ์ˆ˜ ๋งŽ์€ ์ฐธ์กฐ๋ฅผ ๊ฐ€์ง€๊ธฐ ๋•Œ๋ฌธ์— ๋ถ„๋ฆฌํ•ด์„œ usecase๋กœ ์ถ”๊ฐ€ํ–ˆ๋‹ค.

 

์ฐจ์ƒ์œ„ ๊ตฌ์กฐ

๋””ํ…Œ์ผํ•˜๊ฒŒ ์„ค๋ช…ํ•˜์ง„ ์•Š๊ฒ ์ง€๋งŒ,  ์ถ”์ƒ์ ์ธ ๋„ค์ด๋ฐ๋ณด๋‹ค๋Š” ์˜๋ฏธ๋ฅผ ๊ฐ€์ง„ ์ด๋ฆ„์œผ๋กœ ์ตœ๋Œ€ํ•œ ์ง€์–ด๋ณด๋ ค๊ณ  ํ–ˆ๋‹ค.

 

 

Redis

Redis ๊ด€๋ จ๋œ ์„ค์ •๊ณผ ํด๋ž˜์Šค๋“ค์˜ ๊ตฌ์กฐ๋ฅผ ๊ณ ๋ฏผํ•˜๊ณ  ๊ฐœํŽธํ–ˆ๋‹ค.

 

RedisFunction

๊ธฐ์กด ํด๋ž˜์Šค ๊ตฌ์„ฑ

 

์—์„œ๋Š” ํŠธ๋žœ์žญ์…˜์˜ ์ฑ…์ž„๊นŒ์ง€ ๊ฐ–๊ณ  ์žˆ๋Š” RedisFunctionProvider ๊ฐ์ฒด์˜€๋‹ค.

 

ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ๋Š” ์ตœ๋Œ€ํ•œ ์Šคํ”„๋ง์ด ์ œ๊ณตํ•ด์ฃผ๋Š” @Transactional์„ ์ด์šฉํ•˜๋Š” ๊ฒƒ์ด ๋ฐ”๋žŒ์งํ•˜๋‹ค๊ณ  ์˜๊ฒฌ์„ ๋“ค์—ˆ๊ธฐ ๋•Œ๋ฌธ์—, ๋”ฐ๋กœ ํŠธ๋žœ์žญ์…˜ ์ฒ˜๋ฆฌ Aspect๋ฅผ ๊ณผ๊ฐํžˆ ์‚ญ์ œํ–ˆ๋‹ค.

 

public interface RedissonFunctionProvider {

    RLock lock(String lockKey);

    RLock lock(String lockKey, long timeout);

    RLock lock(String lockKey, TimeUnit unit, long timeout);

    boolean tryLock(String lockKey, TimeUnit unit, long waitTime, long leaseTime);

    void unlock(String lockKey);

    void unlock(RLock lock);

    Object getValue(String key);

    void setValue(String key, Object value);

    boolean canUnlock(String lockKey);
}
  • ํŠธ๋žœ์žญ์…˜ ๋ฉ”์„œ๋“œ๋ฅผ ์‚ญ์ œ

 

@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RedisLocked {

    /**
     * ์‚ฌ์šฉ์ž ์ •์˜ redisson lock Key
     */
    String key() default "";

    /**
     * redisson lock ์œ ์ง€ ์‹œ๊ฐ„ <br>
     * ๋‹จ์œ„ : milliseconds
     */
    long leaseTime() default 3000;

    /**
     * redisson lock ํš๋“ ๋Œ€๊ธฐ ์‹œ๊ฐ„ <br>
     * ๋‹จ์œ„ : milliseconds
     */
    long waitTime() default 3000;

}
  • ํŒŒ๋ผ๋ฏธํ„ฐ์— ์ข…์†์ ์ด์ง€ ์•Š๊ธฐ ์œ„ํ•ด ๋ฝ ํ‚ค๋ฅผ ์†์ˆ˜ ๋ฐ›์•„์˜ค๋Š” ์„ค์ •์„ ์ถ”๊ฐ€

 

@Aspect
@Component
@RequiredArgsConstructor
@Slf4j
@Order(value = Integer.MIN_VALUE)
public class RedisLockAspect {
  • @Transactional๋กœ ๋จผ์ € ๊ฐ์‹ธ์ง€๊ณ  ์ตœํ›„์— ์ด Aspect๊ฐ€ ๊ฐ์‹ธ์ง€๋Š” ๊ฒƒ์„ ์›ํ•˜๋ฏ€๋กœ @Order๋ฅผ ์กฐ์ •ํ–ˆ๋‹ค.

 

Redis Store

RedisStore ๊ฐ์ฒด๋Š” ์ถ”์ƒํ™”๋œ RedisTemplate์„ ์ด์šฉํ•˜์—ฌ Redis์— ์ ‘๊ทผํ•ด ๋ฐ์ดํ„ฐ๋ฅผ ๋„ฃ๊ณ  ๋นผ๊ณ  ํ•˜๋Š” ์—ญํ• ์„ ํ–ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ ํ•„๋“œ๋Š” StringRedisTemplate๋งŒ์„ ๋ฐ•์•„๋‘์—ˆ์—ˆ๊ณ , Global ํŒจํ‚ค์ง€์— ๊ตฌ์„ฑํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ RedisTemplate์„ ์‚ฌ์šฉํ•˜๋Š”๋ฐ ํ•œ๊ณ„๊ฐ€ ์žˆ๊ณ , ์ „์—ญ์  ์‚ฌ์šฉ์˜ ์–ด๋ ค์›€๊ณผ SOLID 2์›์น™์— ์œ„๋ฐฐ๋˜์—ˆ๋‹ค๋Š” ์‚ฌ์‹ค์„ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

 

๊ทธ๋ž˜์„œ ์ถ”์ƒํด๋ž˜์Šค๋กœ ํ•œ๋ฒˆ ์ •์˜ ํ•ด๋ดค๋‹ค.

 

@RequiredArgsConstructor
public abstract class RedisStore {

    private final RedisTemplate<String, Object> redisTemplate;
    private final ObjectMapper objectMapper;

    public <T> T getData(String key, Class<T> valueType) {
        ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
        String value = (String) valueOperations.get(key);

        if (StringUtils.isBlank(value)) {
            throw new BadRequestException("ํ•ด๋‹น ํ‚ค์— ๋งž๋Š” ๊ฐ’์ด ์กด์žฌํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.");
        }

        try {
            return objectMapper.readValue(value, valueType);
        } catch (JsonProcessingException e) {
            throw new InternalErrorException(e);
        }
    }

    public void setData(String key, Object value) {
        try {
            String jsonValue = objectMapper.writeValueAsString(value);
            ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
            valueOperations.set(key, jsonValue);
        } catch (JsonProcessingException e) {
            throw new InternalErrorException(e);
        }
    }

    public void setDataExpire(String key, Object value, long duration) {
        try {
            String jsonValue = objectMapper.writeValueAsString(value);
            ValueOperations<String, Object> valueOperations = redisTemplate.opsForValue();
            Duration expireDuration = Duration.ofSeconds(duration);
            valueOperations.set(key, jsonValue, expireDuration);
        } catch (JsonProcessingException e) {
            throw new InternalErrorException(e);
        }
    }

    public Boolean deleteData(String key) {
        return redisTemplate.delete(key);
    }

}
  • ์ผ๋‹จ ๊ฐ€์žฅ ๋งŽ์ด ์‚ฌ์šฉํ•˜๋Š”  String, Object๋กœ ๊ตฌ์„ฑ์„ ํ–ˆ๋‹ค. 
  • ์ œ๋„ค๋ฆญ๊ณผ Object mapper๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ, ์˜ค๋ธŒ์ ํŠธ๋กœ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ๊ฒŒ ์งฐ๋‹ค.

 

@Component
public class UserRedisStore extends RedisStore {

    public UserRedisStore(RedisTemplate<String, Object> redisTemplate, ObjectMapper objectMapper) {
        super(redisTemplate, objectMapper);
    }

}
  • ์‚ฌ์šฉ ํด๋ž˜์Šค๋Š” @Component๋กœ Bean ๋“ฑ๋ก์„ ํ•˜๊ณ  ํ•„์š”ํ•œ ํ…œํ”Œ๋ฆฟ๊ณผ ๋งคํผ๋ฅผ ์ฃผ์ž…๋ฐ›์•„ ์ƒ์œ„ ํด๋ž˜์Šค์— ์ œ๊ณตํ•œ๋‹ค.
  • ๋‹ค๋ฅธ ๋ ˆ๋””์Šค ํ…œํ”Œ๋ฆฟ์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ ์ง์ ‘ ์ฃผ์ž…๋ฐ›์•„ ์žฌ์ •์˜ ํ•˜๋ฉด ๋  ๊ฒƒ ๊ฐ™๋‹ค.

์ด๊ฒŒ ์ตœ์„ ์˜ ๊ตฌ์„ฑ์ธ์ง€๋Š” ์ž˜ ๋ชจ๋ฅด๊ฒ ๋‹ค. 

 

์ดํŽ™ํ‹ฐ๋ธŒ ์ž๋ฐ”๋ฅผ ๋ณด๋ฉด์„œ ์ƒ์†๋ณด๋‹ค๋Š” ์ปดํฌ์ง€์…˜์„ ์‚ฌ์šฉํ•˜๋ผ๋Š” ์•„์ดํ…œ ์ฑ•ํ„ฐ๋ฅผ ์ฝ์—ˆ๋Š”๋ฐ, 

์ƒ์†์„ ๊ตฌํ˜„ํ•  ๋•Œ๋Š” ๋ฉ”์„œ๋“œ์˜ ์žฌ์‚ฌ์šฉ๋ณด๋‹ค๋Š”, ํƒ€์ž…์„ ์œ„ํ•ด ์‚ฌ์šฉํ•˜๋ผ๋Š” ๋ง์ด ์žˆ์—ˆ๋‹ค.

์ง„์ •ํ•œ Is - A ๊ด€๊ณ„์ผ ๊ฒฝ์šฐ๋ฅผ ์ƒ๊ฐํ•ด๋ณด๋ผ๋Š” ๊ฒƒ์ด๋‹ค.

 

๊ทผ๋ฐ ๋‚˜๋Š” ์žฌ์ •์˜๋ฅผ ์•ˆ ํ•  ๊ฒฝ์šฐ ์žฌ์‚ฌ์šฉ์„ ํ•˜๋Š” ๊ฒƒ ๊ฐ™๊ธฐ๋„ ํ•˜๋‹ค. ์•„์ง๊นŒ์ง€๋Š” ์ด๊ฒŒ ์ตœ์„ ์ธ ๋“ฏ ์‹ถ๋‹ค.

 

 

๊ฐ์ƒ

์•„์ง๊นŒ์ง€ ๋ถ€์กฑํ•œ ๊ฒŒ ๋งŽ์€ ๊ฒƒ ๊ฐ™๋‹ค. 

์™„๋ฒฝํ•˜๊ฒŒ ์ฑ„์šธ ์ˆœ ์—†๋‹ค๋Š” ์ƒ๊ฐ์ด ๋“ค์—ˆ๋‹ค. ์ฃผ์–ด์ง„ ์ƒํ™ฉ์—์„œ ์ตœ์„ ์„ ๋‹คํ•  ๋ฟ

์†๋„๊ฐ€ ๋А๋ฆฌ๋‹ค๊ณ  ์ƒ๊ฐํ•˜์ง€ ๋ง๊ณ , ๊ผผ๊ผผํ•œ ๊ตฌ์„ฑ์—๋งŒ ์ง‘์ค‘ํ•˜์ž.

 

์ด์ œ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๊ณ  ์žˆ๋‹ค.

ํ…Œ์ŠคํŠธ ์ฝ”๋“œ๋ฅผ ์งœ๋‹ค๋ณด๋‹ˆ๊นŒ ํด๋ž˜์Šค ๊ฐ„ ๊ฐ•๊ฒฐํ•ฉ์„ ์ง€์–‘ํ•˜๋ผ๋Š” ์˜๋ฏธ๋ฅผ ๋น„๋กœ์†Œ ์•Œ๊ฒŒ ๋˜์—ˆ๋‹ค.

๋” ์ž‘์€ ๊ฐ์ฒด, ์ž‘์€ ํ…Œ์ŠคํŠธ๋ฅผ ์œ„ํ•œ.

 

๋ฐ์ดํ„ฐ ํ…Œ์ŠคํŠธ๋ฅผ ํ•  ๋•Œ์—๋Š” H2 db๋ฅผ ์‚ฌ์šฉํ•ด์„œ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ๋„ ์žŠ์ง€ ๋ง์ž

๋ฐ˜์‘ํ˜•