[๐Ÿ”ฅTroubleShooting - MoodBuddy๐Ÿ”ฅ] ์กฐํšŒ ์†๋„๋ฅผ ๋†’์ด๊ธฐ ์œ„ํ•œ ์บ์‹ฑ, ๊ณผ์—ฐ ์ •๋‹ต์ผ๊นŒ?

._mungยท2025๋…„ 3์›” 13์ผ
0

MoodBuddy

๋ชฉ๋ก ๋ณด๊ธฐ
4/8

๐Ÿ“Œ ํ”„๋กœ์ ํŠธ ์†Œ๊ฐœ

ํŒ€์› : PM(1) / Design(1) / Frontend(2) / Backend(3)
๊ธฐ๊ฐ„ : 2024.03 ~ 2025.03
๋งํฌ : https://github.com/M-ung/MoodBuddy_Server
์„œ๋น„์Šค ๋‚ด์šฉ : ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ์ผ๊ธฐ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ๊ฐ์ • ๋ถ„์„ํ•˜๋Š” ์›น ์„œ๋น„์Šค
์†Œํ†ต : GitHub, Slack, Notion, Discord


๐Ÿ”ฅTroubleShooting๐Ÿ”ฅ

Problems

์ฒ˜์Œ 1์ฐจ ๊ฐœ๋ฐœ ๋‹จ๊ณ„์—์„œ ์„œ๋น„์Šค์— ์žˆ์–ด์„œ ์กฐํšŒ ๊ธฐ๋Šฅ๋“ค์€ ๋ชจ๋‘ Spring Data JPA๋ฅผ ํ™œ์šฉํ•ด DB์— ์ ‘๊ทผํ•˜์—ฌ ์กฐํšŒํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ–ˆ๋‹ค.

๐Ÿ“ ์ผ๊ธฐ ๋ชฉ๋ก ์กฐํšŒ ๊ธฐ๋Šฅ (์บ์‹ฑ X)

    @Override
    public PageCustom<DiaryResQueryDTO> getDiaries(final Long userId, Pageable pageable) {
        return diaryQueryRepository.findDiariesWithPageable(userId, pageable);
    }

ํ•˜์ง€๋งŒ 1์ฐจ ๊ฐœ๋ฐœ ์ดํ›„, ๋‹จ์ˆœํžˆ ์กฐํšŒ ์†๋„๋ฅผ ๋†’์ด๊ณ  ์‹ถ์€ ๋งˆ์Œ์— Redis ์บ์‹ฑ์„ ๋ชจ๋“  ์กฐํšŒ ๊ธฐ๋Šฅ์— ์ ์šฉํ•ด์„œ ๋ฆฌํŒฉํ† ๋งํ–ˆ๋‹ค.

๐Ÿ“ ์ผ๊ธฐ ๋ชฉ๋ก ์กฐํšŒ ๊ธฐ๋Šฅ (์บ์‹ฑ O)

    @Override
    @Cacheable(
            cacheNames = "getDiaries",
            key = "'userId:'+#userId+'_'+'pageable.offset:'+#pageable.offset+'_'+'pageable.pageSize:'+#pageable.pageSize",
            unless = "#result == null"
    )
    public PageCustom<DiaryResQueryDTO> getDiaries(final Long userId, Pageable pageable) {
        return diaryQueryRepository.findDiariesWithPageable(userId, pageable);
    }

Redis ์บ์‹ฑ์„ ์ ์šฉํ•œ ๋•๋ถ„์— ์กฐํšŒ ์†๋„๋ฅผ ์ „๋ณด๋‹ค ํ›จ์”ฌ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์—ˆ๋‹ค. ํ•˜์ง€๋งŒ ๊ตฌํ˜„์„ ํ•œ ํ›„ ์ƒ๊ฐํ•ด ๋ณด๋‹ˆ ์•„๋ž˜์™€ ๊ฐ™์€ ์˜๋ฌธ์ ๋“ค์ด ์ƒ๊ฒจ๋‚ฌ๋‹ค.

1. ๊ธฐํš ํŠน์„ฑ์ƒ ๋ฌด๋“œ๋ฒ„๋””๋Š” ๊ฐœ์ธ์ ์ธ ๊ณต๊ฐ„์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ชจ๋“  ์กฐํšŒ ๊ธฐ๋Šฅ์„ ์บ์‹ฑํ•  ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ?
2. ํ•„ํ„ฐ๋ง ์กฐํšŒ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์กฐ๊ฑด์ด ๋งค๋ฒˆ ๋‹ค๋ฅด๋‹ค. ๊ทธ๋Ÿผ ์บ์‹ฑ์ด ๋ถˆํ•„์š”ํ•˜์ง€ ์•Š์„๊นŒ?
3. ํ˜„์žฌ Redis์˜ TTL๋ฅผ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ฐ™๊ฒŒ ํ•ด๋‘” ์ƒํƒœ์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์„๊นŒ?


How

1. ๊ธฐํš ํŠน์„ฑ์ƒ ๋ฌด๋“œ๋ฒ„๋””๋Š” ๊ฐœ์ธ์ ์ธ ๊ณต๊ฐ„์ด๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๋ชจ๋“  ์กฐํšŒ ๊ธฐ๋Šฅ์„ ์บ์‹ฑํ•  ํ•„์š”๊ฐ€ ์žˆ์„๊นŒ?
์ „์ฒด ์ผ๊ธฐ ์กฐํšŒ ๊ธฐ๋Šฅ์€ ์บ์‹ฑ์„ ์ ์šฉํ•˜์ง€๋งŒ, ๋‚˜๋จธ์ง€ ๊ฐ์ • ์ผ๊ธฐ ์กฐํšŒ, ํ•„ํ„ฐ๋ง ์ผ๊ธฐ ์กฐํšŒ์™€ ๊ฐ™์ด ๋งค๋ฒˆ ๋‹ค๋ฅด๊ฒŒ ์กฐํšŒ๋˜๋Š” ๊ฒฝ์šฐ๋Š” ์บ์‹ฑ์„ ์ œ๊ฑฐํ•  ์˜ˆ์ •์ด๋‹ค.

2. ํ•„ํ„ฐ๋ง ์กฐํšŒ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ์ž…๋ ฅํ•œ ์กฐ๊ฑด์ด ๋งค๋ฒˆ ๋‹ค๋ฅด๋‹ค. ๊ทธ๋Ÿผ ์บ์‹ฑ์ด ๋ถˆํ•„์š”ํ•˜์ง€ ์•Š์„๊นŒ?
ํ•„ํ„ฐ๋ง ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋งค๋ฒˆ ๋‹ค๋ฅด๊ฒŒ ์กฐํšŒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ๋งค์šฐ ํฌ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์บ์‹ฑ์€ ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ํŒ๋‹จ๋œ๋‹ค.

3. ํ˜„์žฌ Redis์˜ TTL๋ฅผ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ฐ™๊ฒŒ ํ•ด๋‘” ์ƒํƒœ์ด๋‹ค. ๊ทธ๋ ‡๋‹ค๋ฉด ์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์ง€ ์•Š์„๊นŒ?
TTL์„ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๊ฐ™์— ์„ค์ •ํ•˜์—ฌ ์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์ด ๋ฐœ์ƒํ•  ๊ฐ€๋Šฅ์„ฑ์ด ํฌ๋‹ค.

์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์ด๋ž€?
์บ์‹œ ์Šคํƒฌํ”ผ๋“œ๋Š” ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋  ๋•Œ, ์—ฌ๋Ÿฌ ์š”์ฒญ์ด ๋™์‹œ์— DB๋กœ ๋ชฐ๋ฆฌ๋ฉด์„œ ๋ถ€ํ•˜๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์„ ๋งํ•œ๋‹ค.
์ฆ‰ ์‰ฝ๊ฒŒ ๋งํ•˜๋ฉด, ์บ์‹œ ๋งŒ๋ฃŒ ํ›„ ์ˆ˜๋งŽ์€ ์š”์ฒญ์ด ๋™์‹œ์— DB๋กœ ๋ชฐ๋ ค ์„œ๋ฒ„๊ฐ€ ๊ณผ๋ถ€ํ•˜๋˜๋Š” ๋ฌธ์ œ์ด๋‹ค.

์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์˜ ์‹œ๋‚˜๋ฆฌ์˜ค๋Š” ์•„๋ž˜์™€ ๊ฐ™๋‹ค.
1. ์บ์‹œ๊ฐ€ ๋งŒ๋ฃŒ๋œ๋‹ค.
2. ๋ชจ๋“  ์š”์ฒญ์ด ์บ์‹œ๋ฅผ ์ฐพ์ง€๋งŒ, ๋ฐ์ดํ„ฐ๊ฐ€ ์—†๋‹ค.
3. ๋™์‹œ์— ๋ชจ๋“  ์š”์ฒญ์ด DB์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค ํ•œ๋‹ค.
4. DB ๋ถ€ํ•˜ ๊ธ‰์ฆํ•œ๋‹ค.

์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•๋“ค์„ ๊ณ ๋ คํ–ˆ๋‹ค.

1. ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ํ†ตํ•ด ์บ์‹œ๋œ ํ…Œ์ดํ„ฐ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ์ฏค์— ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•
2. ์บ์‹œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๋žœ๋ค์œผ๋กœ ๋ฐฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•

1. ์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ํ†ตํ•ด ์บ์‹œ๋œ ํ…Œ์ดํ„ฐ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ์ฏค์— ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•
์Šค์ผ€์ค„๋Ÿฌ๋ฅผ ํ†ตํ•ด ๋งค๋ฒˆ ์บ์‹œ๋œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ œ๊ฑฐ๋  ๋•Œ์ฏค์— ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ์‹์€ ๊ณผํ•œ ๋ฐฉ์‹์ด๋ผ๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
๊ทธ ์ด์œ ๋Š” ๋งŒ์•ฝ ์‚ฌ์šฉ์ž๊ฐ€ ์žฅ๊ธฐ๊ฐ„ ์ ‘์†ํ•˜์ง€ ์•Š์€ ์‚ฌ์šฉ์ž์ž„์—๋„ ๋ถˆ๊ตฌํ•˜๊ณ  ๊ณ„์† ์บ์‹ฑ์„ ๊ฐฑ์‹ ํ•ด ์ฃผ๋Š”๊ฑด ๋ถˆํ•„์š”ํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

2. ์บ์‹œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๋žœ๋ค์œผ๋กœ ๋ฐฐ์ •ํ•˜๋Š” ๋ฐฉ๋ฒ•
์บ์‹œ ๋งŒ๋ฃŒ ์‹œ๊ฐ„์„ ์‚ฌ์šฉ์ž๋งˆ๋‹ค ๋žœ๋ค์œผ๋กœ ๋ฐฐ์ •ํ•ด ์ฃผ๋Š”๊ฑด ์ข‹์€ ๋ฐฉ์•ˆ์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค. ์ด๋ ‡๊ฒŒ ์ ์šฉํ•œ๋‹ค๋ฉด ๋™์‹œ์— ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ฑํ•  ์ผ์„ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

โœจ ์ƒˆ๋กœ์šด ๋ฐฉ์•ˆ โœจ
๋ฌด๋“œ๋ฒ„๋””๋Š” ์‚ฌ์šฉ์ž๋งŒ์˜ ๊ณต๊ฐ„์ด๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์‚ฌ์šฉ์ž๊ฐ€ ์ผ๊ธฐ๋ฅผ ์ž‘์„ฑ, ์ˆ˜์ •, ์‚ญ์ œํ•˜๋Š” ์ผ์ด ์—†๋‹ค๋ฉด ์ผ๊ธฐ ๋ชฉ๋ก๋“ค์€ ํ•ญ์ƒ ๋˜‘๊ฐ™๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— ์Šค์ผ€์ค„๋Ÿฌ๋กœ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜์ง€ ์•Š๊ณ  TTL์„ ๊ธธ๊ฒŒ ์œ ์ง€ํ•œ ํ›„ ์ผ๊ธฐ ์ €์žฅ, ์ˆ˜์ •, ์‚ญ์ œํ•  ๋•Œ๋งˆ๋‹ค ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐฑ์‹ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
๋˜ ํ˜น์‹œ๋ผ๋„ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋Š” ์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์„ ๋ฐฉ์ง€ํ•˜๊ณ ์ž TTL ์‹œ๊ฐ„์„ ๋žœ๋ค์œผ๋กœ ๋ฐฐ์ •ํ•˜๋Š” ๊ฒƒ์ด๋‹ค.


Process

๋จผ์ € ๋งค๋ฒˆ ์กฐ๊ฑด์ด ๋‹ฌ๋ผ์ง€๋Š” ์ผ๊ธฐ ์กฐํšŒ ๊ธฐ๋Šฅ์—๋Š” ์บ์‹ฑ ์ ์šฉ์„ ์ œ๊ฑฐํ–ˆ๋‹ค.

    @Override
    public PageCustom<DiaryResQueryDTO> getDiariesByEmotion(final Long userId, DiaryEmotion diaryEmotion, Pageable pageable) {
        return diaryQueryRepository.findDiariesByEmotionWithPageable(userId, diaryEmotion, pageable);
    }

    @Override
    public PageCustom<DiaryResQueryDTO> getDiariesByFilter(final Long userId, DiaryReqFilterDTO requestDTO, Pageable pageable) {
        return diaryQueryRepository.findDiariesByFilterWithPageable(userId, requestDTO, pageable);
    }

๊ทธ๋ฆฌ๊ณ  ๋‚ ์งœ ์ˆœ์œผ๋กœ ์˜ค๋ž˜๋œ ์ˆœ, ์ตœ์‹  ์ˆœ์œผ๋กœ ์ผ๊ธฐ๊ฐ€ ์บ์‹ฑ ์กฐํšŒ๋  ์ˆ˜ ์žˆ๊ฒŒ ์•„๋ž˜์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ–ˆ๋‹ค.

    @Override
    @Cacheable(
            cacheNames = "diaries",
            key = "'userId:' + #userId + '_sort:' + #isAscending + '_page:' + #pageable.pageNumber + '_size:' + #pageable.pageSize",
            unless = "#result == null"
    )
    public PageCustom<DiaryResQueryDTO> getDiaries(final Long userId, boolean isAscending, Pageable pageable) {
        return diaryQueryRepository.findDiariesWithPageable(userId, isAscending, pageable);
    }

๊ทธ๋ฆฌ๊ณ  ์ผ๊ธฐ ์ €์žฅ, ์ˆ˜์ •, ์‚ญ์ œ ์‹œ ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ดˆ๊ธฐํ™”ํ•˜๊ณ  ์ฒซ ํŽ˜์ด์ง€๋งŒ ์บ์‹œ์— ๊ฐฑ์‹ ๋  ์ˆ˜ ์žˆ๋„๋ก ๊ตฌํ˜„ํ–ˆ๋‹ค.

@Service
@RequiredArgsConstructor
public class RedisServiceImpl implements RedisService {
    private final RedisTemplate<String, String> redisTemplate;
    private final DiaryQueryService diaryQueryService;
    private static final String DIARIES_CACHE_PREFIX = "diaries::userId:";
    private static final String DIARY_COUNT_CACHE_PREFIX = "diary_count:userId:";
    private static final int PAGE_SIZE = 20;

    @Override
    public void deleteDiaryCaches(Long userId) {
        deleteCacheByUserIdAndCacheName(userId);
        deleteCountCacheByUserId(userId);
        cacheFirstPage(userId);
    }

    private void deleteCacheByUserIdAndCacheName(Long userId) {
        var pattern = DIARIES_CACHE_PREFIX + userId + "*";
        var keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    private void deleteCountCacheByUserId(Long userId) {
        String pattern = DIARY_COUNT_CACHE_PREFIX + userId;
        var keys = redisTemplate.keys(pattern);
        if (keys != null && !keys.isEmpty()) {
            redisTemplate.delete(keys);
        }
    }

    private void cacheFirstPage(Long userId) {
        diaryQueryService.refreshDiariesCache(userId, false, PageRequest.of(0, PAGE_SIZE));
    }
}

๋˜ ๋ชจ๋“  ์‚ฌ์šฉ์ž๊ฐ€ TTL์ด ๊ฐ™์œผ๋ฉด ์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ์ด ์ผ์–ด๋‚  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ TTL์„ 24์‹œ๊ฐ„์„ ๊ธฐ์ค€์œผ๋กœ ๋žœ๋ค ์ €์žฅํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ–ˆ๋‹ค.

    @Bean
    public RedisCacheManager cacheManager(RedisConnectionFactory cf) {
        Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();
        int randomTTL = 24 + (int) (Math.random() * 5);
        cacheConfigurations.put("diaries", defaultCacheConfiguration().entryTtl(Duration.ofHours(randomTTL)));
        return RedisCacheManager.builder(cf)
                .cacheDefaults(defaultCacheConfiguration())
                .withInitialCacheConfigurations(cacheConfigurations)
                .build();
    }

Result

์ •์ƒ์ ์œผ๋กœ ์กฐํšŒ ๋ฐ ์บ์‹œ์— ์ €์žฅ๋˜๋Š” ๊ฑธ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.


Thoughts

์บ์‹ฑ ์กฐํšŒ๋งŒ ํ•˜๋ฉด ์†๋„๊ฐ€ ๋นจ๋ผ์ง€๊ณ  ์†๋„๋ฅผ ๊ฐœ์„ ๋งŒ ํ•œ๋‹ค๋ฉด ์ด๊ฒƒ์ด ์ •๋‹ต์ด๋ผ๊ณ  ์ƒ๊ฐํ–ˆ๋‹ค.

ํ•˜์ง€๋งŒ ๋‹จ์ˆœํžˆ ์บ์‹œ์— ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•˜๊ณ  ์กฐํšŒํ•˜๋Š” ๊ฑด "์บ์‹œ ์Šคํƒฌํ”ผ๋“œ ํ˜„์ƒ"๊ณผ ๊ฐ™์€ ๋ฌธ์ œ๋ฅผ ์ดˆ๋ก€ํ•  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ์บ์‹œ ๋ฐ์ดํ„ฐ๋ฅผ ์ €์žฅํ•  ๋•Œ ์‹ ์ค‘ํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฑธ ๋ฐฐ์šธ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๋˜ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฅผ ์บ์‹œ์— ์ €์žฅํ•˜๊ธฐ๋ณด๋‹จ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์บ์‹ฑ ์ ์šฉ์ด ํ•„์š”ํ• ์ง€ ์•ˆ ํ• ์ง€ ํŒ๋‹จํ•˜๋Š” ๊ฒƒ์ด ์ค‘์š”ํ•˜๋‹ค๋Š” ๊ฑธ ๋ฐฐ์› ๋‹ค.

์„ฑ๋Šฅ๋งŒ ์ค‘์š”์‹œํ•˜๋Š” ๊ฐœ๋ฐœ์ž๊ฐ€ ์•„๋‹Œ, ์ƒํ™ฉ์— ๋”ฐ๋ผ ์„ค๊ณ„๋ฅผ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฐœ๋ฐœ์ž๋กœ ์„ฑ์žฅํ•ด์•ผ๊ฒ ๋‹ค๊ณ  ๋‹ค์งํ–ˆ๋‹ค.


profile
๐Ÿ’ป ๐Ÿ’ป ๐Ÿ’ป

0๊ฐœ์˜ ๋Œ“๊ธ€

๊ด€๋ จ ์ฑ„์šฉ ์ •๋ณด