ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 2024 if(kakaoAI) Day 3 참석 후기 - 백엔드 위주
    개발 일기장/직장 생활 2024. 10. 28. 04:09
    기억과 필기에 의존해서 작성한 거라 발표자가 전달한 정보와 다른 부분이 있을 수 있음!

     

    기업에서 개최하는 컨퍼런스를 처음 가보았다.

     

    갔다온지 얼마 안됐을 때 후기를 정리해두려한다. 수기로 썼으면 날림으로 필기해놨을 수준으로 메모장에 적혀있다.

     

    올해의 핵심 키워드는 역시 AI였다. (AI 모르는 백엔드 개발자는 웁니다 ㅜ)

     

    백엔드 개발자가 들을만한 주제는 Day 3에 몰려있어 Day 3를 신청했는데 당첨되어 ifKakao에 다녀왔다. 글을 쓰려고보니 올해는 ifKakao가 아니라 if(kakaoAI)인걸 알아차렸다.

     

    장소는 용인시 고기리에 위치해있는 AI 캠퍼스라고 하는데, 접근성이 떨어져서 카카오측에서 셔틀을 제공해주었다.

     

    요즘은 기업 테크 컨퍼런스는 코엑스에서 많이하는 추세인거 같은데 역세권과 멀리 떨어진 장소라 의외였다. 좋게 얘기하면 뚝심이 있달까... 판교로 출퇴근하는 나는 그다지 불편하지 않았지만, 외부인들 입장에서는 매우 불편했을지도 모르겠다.

     

    그래도 셔틀은 특정 시간대에 20분 간격으로 운행해서 셔틀을 타는데 불편함은 없었다.

     

    건물이 아주 이뻤다.

     

    컨퍼런스를 지원해주시는 직원분들이 많으셨다. 직원분들 모두 활기차게 맞이해주셔서 나도 기분이 좋았다. 입구에 간식을 나눠주고 계셨다. 간식은 견과류와 편의점 커피였고, 물은 얼마든지 가져갈 수 있게 비치되어있었다. 소정의 기념품도 있었는데, if(kakaoAI)가 그려진 홀로그램 스티커와 신발 가방(?)을 받았다. (기념품은 솔직히 쏘쏘..)

     


    위에 찍은 건물 아래가 폴딩 도어로 되어 있어 오픈 테라스로 만들 수 있었다. 큰 스크린의 맞은 편(내 뒷편)은 뚫려있다. 컨퍼런스 시작까지는 폴딩 도어가 열려있었는데 바깥 찬 바람이 계속 들어와 추웠다.

     

    카카오 CTO의 기조 연설로 Day 3가 시작했다. 카카오 계열사(카카오 엔터프라이즈, 카카오뱅크, 카카오페이, 카카오엔터테이먼트, 카카오 헬스케어) CTO들이 차례로 나와 각 회사에서 AI를 활용해 어떤 서비스를 하고 있는지 설명했다. 카카오 모빌리티는 CTO의 부재(최근 퇴사를 하셨다)로 다른 분이 나오신듯했다.

     

    AI 용어인지 그 도메인의 용어인지 몰라서 일단 필기하고 나중에 찾아보려고 들리는대로 써놨다 ㅎ

     

    카카오 자회사의 AI 적용한 사례 세션 관련

    더보기

     

    카카오 페이

    - FDS, ADS와 Adaptive ML

    - 사기 거래 방지를 위해 룰과 ML을 서로 배타적으로 병렬 실행해 두개의 결과를 비교한다

    - MoE 아키텍쳐를 적용함 -> 찾아봤던 링크: https://developer.nvidia.com/ko-kr/blog/applying-mixture-of-experts-in-llm-architectures/

     

    카카오뱅크

    - GenAI(생성형 AI) 가드레일: AI를 보호하는 장치, 부적절한 콘텐츠가 LLM으로 도달하지 않도록 차단한다. 기술보단 정책이다.

       - 프롬프트 인젝션 방지

    - XAI(https://www.ibm.com/kr-ko/topics/explainable-ai)

     

    카카오 모빌리티

    자율 주행을 테스트를 계속하고 있다. 혹시나 모를 사고에 대비해 항상 safety driver가 동승한다고 한다.

    구글 웨이모, 우버의 기술력에 미칠 수 있다고 생각하냐는 질문에 경쟁보단 공생이 목표라한다. 웨이모나 우버가 국내 또는 아시아에 진입할 때 그들보다 잘 가공된 데이터를 제공하는 방식으로?

     

    카카오 헬스케어

    '파스타' -> 건강관리 서비스

    디바이스로 혈당을 측정해주고, 챗봇으로 유저가 능동적으로 건강관리를 할 수 있도록 해준다.

    매번 건강관리 기록(먹은 음식, 운동양 같은?)을 타이핑하기 귀찮으니 음성으로 기록 할 수 있는 피처를 넣을 예정이다였는지 이미 존재한다고 했는지... 기억이 안난다.

     

    카카오 엔터프라이즈

    AI를 잘 모르는 나에게 흥미로웠던건 카카오엔터테이먼트에서 사용하는 Helix shorts였다. 웹툰, 웹소설을 AI가 읽어서 유튜브 쇼츠 같은 콘텐츠를 만들어내는 것이다. Vision AI(AI가 이미지를 인식하고 해석하는 기술)를 통해 캐릭터의 감정과 특징을 캐치해내서 요약하고 적절한 배경음악을 선택한단다. '이야 AI가 어떻게 웹툰에 나오는 캐릭터의 감정까지 캐치하지?' 란 생각이 들었다.

    콘텐츠 하나 만드는데 시간은 3시간, 비용은 5만원이라고 한다. 모든 쇼츠 콘텐츠를 AI가 만드는 것은 아니고 트래픽의 5%만 A/B 테스트를 하는데 AI가 만든 콘텐츠가 더 좋은 결과를 나타낼 때가 있단다.

     

    버추얼 아이돌도 만들어보려 하는데 단순히 노래만 부르는게 아니라 팬들과 소통할 수 있어야 의미가 있는데 현재 기술로는 어렵다고 한다. 하지만 Helix shorts도 작년에는 현재 기술로는 불가능하다고 했다는데 지금 만든걸 보니

    AI 기술 발전이 빨라 빠른 시일 내에 가능할지도?

     

    점심 양은 작지만 맛있었다

     

    깡시골이라 점심 먹고 가볍게 호수 근처 산책

     


    동시에 세션이 진행되므로 모든 세션은 정리하지 못하고 참관했던 세션만 정리했다.

     

    백엔드 위주로 참관했고, 재밌어보이는 세션을 듣기도 했다. 근데 다 같은 컨퍼런스룸이라 하루종일 한 장소에만 있었다...

     

    세션1. 대용량 트래픽 아니면 안 보셔도 됩니다! 선물하기 서비스 캐싱 전략

    이희관, 이세희 | 카카오

    더보기

     

    이 컨퍼런스룸을 떠나질 못했다...

     

    "대용량 트래픽" 백엔드 개발자의 눈길을 끄는 치트키. 마법의 용어.

     

    들은 세션 중 사람이 제일 많았던 세션이었다. 

    • 로컬 캐시와 원격 캐시를 적절히 혼용해서 사용. 
    • 자주 사용하고 범용적인 데이터는 로컬 캐시를 활용하고 캐시간의 동기화는 주키퍼 활용.
    • 데이터 변경이 일어나면 주키퍼를 통해 캐시를 만료 시켜서 새로운 캐시로 신선도 유지

     

    다음 분 발표

    • 배너 광고를 보여줄 캐싱 전략으로는 PER 알고리즘 전략을 쓰려고 했으나 채택하지 않았음. 
      • 이유를 설명해주셨는데 기억이 잘 나지 않는다.(뒤에 설명을 생각하면 랜덤하게 갱신하지 않고 조회수에 따라 관리하고 싶었던거지 않을까)
      • 같이 보면 좋을 듯한 글(PER  알고리즘 구현)
    • 레디스의 sorted set 데이터구조를 활용해 프로모션과 누적 조회 수를 관리함
    • key가 늘어나면 성능이 떨어지므로 시간대별로 파티션을 나누어 관리
    • 스코어 가중치(누적 조회수가 인자라 했던 것 같다)를 계산해서 캐시 갱신 관리
    • 가용성을 높이기 위해 먼저 로컬 스토리지에 담아두고 일정시간마다 저장함(로컬 스토리지를 버퍼 라이터 역할로 사용한다는 듯)
      • 어떤 데이터인지는 써두질 못했는데 정황상 누적 조회수를 바로 레디스에 넣지 않는다는 얘기 아니었을까
    • 갱신 트리거로 rabbitMQ 사용

    통계를 이용해 이탈을 방지할 수 있을까? SMART STATS 개발기

    송대섭 | 카카오게임즈

    더보기
    • 게임의 체감 난이도는 국가마다 다르다. 어떤 나라에는 어려워도 다른 나라에는 너무 쉬울 수 있다
      • 적정 난이도를 맞추기 어렵다 
      • 국가는 말안해주셨지만, 왠지 한국인은 잘하고 서양인은 못한다일거 같은 느낌적인 느낌
    • 게임이 어려워서 이탈하는 경우 런칭 초기일수록 빠르게 대응해야한다
    • 직접적으로 도움을 준다(아이템 지원) -> 실질적으론 효과가 없다
    • 방치하면 탈퇴하고 지원해준다고 도움이 되진 않는다
    • 통계를 통해 해결방법을 찾고자 했다.
      • 평균에서 벗어난 플레이를 하는 사람들이 주로 탈퇴했다
      • 방향을 못잡는 초보에게 도움을 줄 수 있지 않을까
    • 유저에게도 통계를 제공하는 서비스를 만듦
    • 통계를 통해 초보는 방향성을 찾고 고수는 자신의 수준을 알 수 있다 

     

    언제, 어떻게, 어떤 통계를 제공할 것인가

    언제(시점)

    메인 퀘스트나 던전 클리어 시점, 내가 지금 막혀있는 퀘스트

    -> 통과를 위한 필요한 스펙을 알 수 있다.

     

    어떤(데이터의 종류)

    나와 비슷한 사람의 데이터(통계에서 핵과금러, 봇, 매크로는 빼버림)

    유사한 플레이 성향으로 분류(안정지향적 vs 도전지향적, 솔플 vs 파티플)

    노력으로 따라할 수 있는 행동(유저가 과금 유도로 해석하지 않도록)

     

    어떻게

    확장성과 유연성을 고려해야했음

    확장성: 게임 내에 다른 게임이 추가될 수 있음

    유연성: 이벤트 기반으로 발생하는 로그를 수집(정해진 스키마로 로깅하기 보다는)

     

     

    적절한 시점에 통계에 대한 알림을 주고 싶었다

    데이터가 없어서 머신러닝을 시킬수 없었음

    그래서 1차 스펙으로 데이터를 쌓고 2차에 제대로 써보자의 기조로 출발

    => 적절하다 싶은 타이밍에 A/B 테스트를 통해 알림을 보내봄

     

    결론은 통계를 제공해준 유저들이 포기하는 허들을 넘어서 유지하는 경우가 많았다.


    모두를 위한 게임 데이터 검색 시스템

    강동진, 유선정 | 카카오게임즈

    더보기

    게임 데이터 분석은 속도가 생명이다.(그만큼 중요하다)

    • 어뷰징 차단, 초기 결제가 매출에 이어져서 결제 패턴 분석 중요 

    BI(Business Intelligence: 조직이 더 나은 의사결정을 내리고, 정보를 기반으로 행동을 취하고, 보다 효율적인 비즈니스 프로세스를 구현할 수 있게 해주는 역량을 의미) 담당자들이 데이터 분석이 빠르게 필요함

     

    해결방안으로 데이터를 담당자들이 직접 사용할 수 있도록 오픈했다.

    하지만, sql이나 데이터 구조를 배워야해서 막상 원활하지 않았다

    => 자연어 기반 게임 데이터 검색 시스템을 만들었다(대략 이해한 바로 사람이 평소 쓰는 말을 사용해도 알아먹는 시스템?인듯)

     

    자연어 기반 게임 데이터 검색 시스템

    기능 1. 데이터 추출

    첫번째 시도: Text to SQL. 자연어를 입력하면 쿼리로 변환해줌

    문제점: LLM의 Hallucination

    AI 할루시네이션은 AI 모델이 생성하는 잘못되거나 오해의 소지가 있는 결과를 말함. 이러한 오류는 불충분한 학습 데이터, 모델의 잘못된 가정, 모델 학습에 사용된 데이터의 편향 등 다양한 요인으로 인해 발생할 수 있음.

     

    정확도가 보장되지 않아서 의미가 없다…

     

    두번째 시도: 정확성 담보를 위해 쿼리 템플릿 사용

    문제점: 사용자가 원하는 템플릿이 없다면?

    해결방안: 쿼리 빌더 사용

     

    기능 2. 데이터 탐색

    템플릿과 빌더 방식으로 해결 안되는 부분이 많음(사람이 하는 질문은 추상적이고 정형화 하기 어려운게 많다)

     

    질문을 정형화하지 않고 직관에 의해 질문 해버린다면?

    1. 데이터를 쌓는데 정형화된 스키마에 맞춰서 쌓지 않는다.(NoSQL에다 로그 쌓듯이 쌓는다는 말인가?)

    2. 게임 유저 중심의 질문이 많음

    • 여러 테이블에 필요한 특성을 모아 유저 프로필을 만든다. 벡터 DB를 사용한다

    유사도를 계산해서 인간 친화적 질문의 문제를 처리할 수 있지만, 모든 질문을 해결하진 못한다는 한계가 있다.


    GraalVM 도입을 JVM 백엔드 애플리케이션의 구동 초기 성능 문제 해결하기

    김동현 | 카카오

    더보기

    발표자분이 떨렸는지 중간 중간 소리나게 한숨 쉬어서 귀여웠음.

    이슈

    서버가 웜업이 안되서 발생하는 이슈가 있었다

    (기존의 일반적인 JVM은 실행하는 순간에 바이너리코드로 변환(JIT 컴파일)하기 때문에 기동 초기에 성능이 안나오는 많이 알려진 이슈)

     

    HotSpot JVM과 GraalVM 비교

    JIT를 사용하는 HotSpot JVM보다 GraalVM이 런타임 환경에서 빠르다 

    왜냐면 GraalVM은 컴파일 때 미리 바이너리코드로 다 바꿔두기 때문.

    단점으로 동적으로 런타임에 생성되는 코드에 대한 대응은 할 수 없음

    예시)

    • 자바 리플렉션을 통한 코드 생성
    • 로그백처럼 설정에서 구체적인 클래스 경로를 입력하는데 로그백은 해당 클래스를 동적으로 생성하기 때문에 ClassNotFound 발생한다 

    위에서 설명한 GraalVM의 단점 해결 방법은?

    동적으로 생성되는 것들을 컴파일 타임에 모두 만든다

    => Reachability Metadata라고 한단다. Tracing Agent를 통해 Reachability Metadata를 찾아낸다.

     

    Reachability Metadata를 얻으려면?

    1. Tracing Agent를 적용한 상태로 프로그램 시작, 종료

    2. Tracing Agent를 적용한 상태로, 높은 커버리지로 작성된 테스트 프로그램을 돌린다.

     

    벤치마크 결과

    초반에는 GraalVM보다 처리율이 떨어지나 최종적으로는 HotspotJVM이 최적화되어 처리율이 더 좋았다.

    GraalVM은 처음부터 끝까지 안정화된 상태긴하다.

     

    시간이 지날수록 GraalVM이 HotspotJVM보다 처리율이 떨어지는데 GraalVM을 최적화할 수 있는 옵션(PGO: profile-guided optimization)을 적용하니 처리율도 높아졌다.

     

    소감: GraalVM으로 웜업이 안된 이슈를 해결할 수 있으나 손이 가는게 많다로 느꼈음


    지연이체 서비스 개발기: 은행 점검 끝나면 송금해드릴게요!

    박소현 | 카카오페이

    더보기

    기존에도 지연이체 서비스가 있었지만, 사내에서 rabbitMQ을 사용하고 있었다.

    어느날, 전사 정책으로 kafka로 바꿔야했다.

     

    그래서 발생한 문제점

    파티션이 여러 개인 상황에서 프로듀서가 메시지를 중복 발행하면 2개 이상의 컨슈머가 중복 처리할 수 있기 때문에 락을 사용했다

    유저에 대한 락을 걸려고 하니 유저마다 지연이체를 복수로 걸 수 있어서 유저락을 걸면 여러개의 지연이체 중 하나만 처리될 수도 있음

    => 카프카 파티션 키를 사용해 하나의 컨슈머에서 하나의 유저에 대한 지연 이체를 처리하도록 함

     

    컨슈머가 실행속도가 느려서 예상 처리 시간보다 길었음

    => 처리한 방법

    1. 파티션과 컨슈머를 늘렸음(파티션 갯수와 컨슈머 수는 일치하도록 - 요건 카프카 권장사항이다)

    2. 카프카 메시지를 건바이건으로 가져오지 않고 배치 사이즈로 메시지를 가져오도록 함

    3. 컨슈머 1개당 패러럴하게(병렬처리) 송금처리함


    카카오페이는 어떻게 수천만 결제를 처리할까? 

    양세열 | 카카오페이 

    더보기

    안전한 결제를 위해 동시성 이슈 처리가 필요하다

    요즘은 MSA 환경이다. 여러 시스템이 물려있는 트랜잭션 처리를 위해서는 DB락보단 분산락을 사용한다.

    분산락 구현을 위해 Redis와 Spring을 사용하므로 자바 레디스 클라이언트인 Redisson 라이브러리를 사용했다.

     

    여러 종류의 락을 만들어서 사용하고 있다.

    예를 들어, 유저에 대한 락이나 결제 ID 분산락 등

     

    분산락 구현 방법

    1. Spring AOP 사용해서 애너테이션으로 구현

    AOP를 활용해 애너테이션으로 쓰면 불편한 점

    • 적용이 어려움
      • AOP를 적용하려면 프록시 객체를 만들 수 있어야해서 public 메소드에만 적용됨.
    • 락의 key를 파라미터로 사용하려면 적용이 어려움
      • String으로 보통 받을텐데 잘못된 값이 들어와도 인지하기 어려움(런타임에 알게 되겠지?)
    • 락 장시간 점유로 불리한 성능
      • 락이 불필요한 영역이 있는데 같이 묶여버림
      • 불필요한 영역까지 락이 걸려있어 응답시간이 늦어짐
    • 기능 수정 및 리팩터링 주의 필요
      • 함수의 첫번째 파라미터를 락 키로 잡기로했는데 리팩토링 한다고 파라미터를 추가하면 락 키가 잘못 적용됨

     

    2. 해결방법 - 함수형 분산락 사용하기

    필요한 곳에만 적용할 수 있도록 Util 성 함수처럼 만든다. (분산락 함수의 인자를 적용할 함수로 받아서 실행한다.)

    무슨 락을 거는지 알 수 있도록 함수를 락의 종류마다 분리한다. (함수명에 'userLock' 같은 식으로)

    장점

    • 락이 필요한 상황에만 적용할 수 있고 private 함수에도 적용할 수 있다
    • 큰 트래픽을 처리한다면 필요한 부분만 락을 적용함으로써 불필요한 로직까지 락을 걸었을 때 비해서 성능이 훨씬 좋아진다

    나도 회사에서 분산락을 사용하고 있다. 내부사정으로 DB에 유니크 키를 걸 수 없어서 애플리케이션 단에서 유일성 보장을 해야했다.

    카카오페이 결제에 비하면 아주아주 작은 트래픽을 받고 있는 서비스지만.

    난 반대로 함수형으로 사용하다 애너테이션으로 변경했다. 스펙과 요구사항이 달랐다.

    • 여러 서비스의 걸쳐져 있지 않아서 락이 긴 시간 걸릴 부분이 없었다
      • 대부분 서비스에서 사용하는 DB의 여러 테이블
    • 락을 사용하는 클래스마다 락을 제공하는 클래스의 의존성 선언을 여기저기 선언하기 싫었다.
      • Spring의 dependency Injection을 하려면 생성자에 사용하는 클래스를 선언해주어야하니까
    • 동시성이 보장되어야하는 부분이 서비스 내에 많지 않아서 락 키가 헷갈릴 일이 없었다.

    선택은 각 개발자의 몫으로..


    카카오톡 펑 개발기

    김중선, 서상민 | 카카오

    더보기

    펑을 개발할 때 푸시, 풀 모델에 대해 고민을 했었다.

    (푸시, 풀 모델을 설명할 때 메시지큐 시스템(예: RabbitMQ)과 발행구독 시스템(예: Kafka)으로 설명하곤 한다)

     

    푸시

    생성은 느리지만 조회가 빠름

    공간적 특징 -> 1:N

     

    생성이 빠르지만 조회가 느림

    공간적 특징 -> 1:1

     

    펑을 생성할 때 푸시해준다면?

    방법: 생성자가 펑을 만들어서 조회자 피드로 푸시해서 조회자 피드에 펑을 저장

    장점: 미리 저장해뒀기 때문에 조회자가 빠르게 조회할 수 있다

    단점: 조회자가 많다면 서버는 더 많은 일을 해야한다

    24시간 안에 조회 안하면 쓸모없이 데이터를 쌓은 게 되버린다

     

    펑을 조회할땐 풀로 처리한다면?

    방법: 조회 시점에 피드를 생성한다.

    장점: 필요할 때만 데이터를 생성한다.

    단점: 조회시간이 오래걸린다

     

    푸시 모델로 선택. 왜?

    sns를 사용하면 조회가 97%다. 생성은 3%

    푸시 모델을 사용할 강력한 근거가 된다.(그래야 조회가 빠르니깐)

    아직 펑에서 인플루언서가 있진 않은데 인플루언서가 있었다면 푸시 모델도 쉽진 않았을 것이다.(일반적인 조회자보다 훨씬 많은 조회자에게 푸시해야해서)

    조회자 피드에 본문을 다 넣어주는게 아니라 펑 본문 1개를 db에 저장하고 그 id 값만 조회자들 피드에 넣어준다.

     

    조회 비용 줄이기

    펑은 수정은 못하고 생성, 삭제만 된다는 조건을 활용해 캐싱을 사용한다.

    펑 본문 DB를 조회하는 경우는 클라이언트에 캐싱되지 않은 id를 in 절로 검색한다.(캐싱된 펑은 제외됨)

     

    펑 피드는 언제 변할까?

    => 펑을 생성할 때 또는 삭제할 때

    두 이벤트가 발생하지 않는다면 DB를 조회할 필요도 없다.

    리비전 개념을 사용해서 클라이언트와 서버 간의 버전 차이가 있을 때만 캐시를 동기화한다

    (리비전 DB와 공개대상DB(누구에게 게시물을 공개할지 저장해두는 DB인듯), 펑 본문 DB를 분리되어있다)

     

    리비전 DB에서 조회한 버전 값이 같다면 본문 DB를 볼 필요 없다.

    (이 얘기를 듣고 낙천적 Locking이 떠올랐다.)


    펑 생성의 느린 원인

    펑 본문 DB 생성은 빨라도 공개해야할 대상에 대한 정보를 가진 공개 대상 DB, 리비전 DB에서 부하가 발생한다.

     

    방안

    공개대상 DB, 리비전 DB에 대한 정보 저장은 꼭 sync일 필요 없어서 async 처리했다

    펑 본문 DB 저장만 성공하면 생성자에게는 성공 응답을 준다


    렌더링 비용 줄이기

    카카오톡 프로필의 경우, 기본 배경 이미지에 각각 이모지 위치를 저장해놓고 이에 맞춰서 렌더링 해주고 있었다.

     

    방안

    펑은 수정 기능이 없기 때문에 처음부터 통이미지로 만들어서 사용

     

    하지만, 이미지에 인터랙션이 들어가면(인스타에 실시간 투표하기 같은?) 통이미지도 어렵다. 이럴 경우에는 카카오톡 프로필처럼 인터랙션 부분만 따로 처리한다.


    셔틀 타기 전 노을이 이뻐서 찍어봤다

     

    후기를 좀 검색해보니 실망했다는 리뷰들이 꽤 보인다. 첫 기업의 기술 컨퍼런스 참석(지원은 맨날 하지만 맨날 떨어짐 ㅜ)이라 비교군이 없어서 잘 모르겠지만 만족스러웠다. 금방 말한 것과 같이 그 만족이 ifKakao라서기보다는 '컨퍼런스 참여가 중요하다'를 느낀 만족감이다.

     

    회사 일만 하는 것에 머물러 있으면 몰랐을 기술, 문제와 해결방안, 트렌드, 회사 외 사람들의 사고에 대해 알 수 있는 기회를 가진 것이 좋았던 컨퍼런스였다.

     

    짧게 쓰려고 했는데, 정리한 것들을 빠짐없이 쓰려다보니 길어져서 필요한 부분만 볼 수 있도록 줄임말로 처리하였다.

     

Designed by Tistory.