
WebGL과 함께 배우는 컴퓨터 그래픽스 (2)
얼마 전에 충격적인 사실을 알았습니다. 지난 기고에 참고했던 문서가 번역본을 제공하더라고요.알고보니 제가 애써서 번역할 필요가 없었습니다. 코드를 이해하는데 꼭 필요한 기반 지식을 설명하는데 큰 의의가 있기는 했지만요.이번에는 라이팅(Lighting)와 머티리얼(Material)에 대해 다루려고 합니다.컴퓨터 그래픽스에서 어떤 물체를 사실적으로 표현하기 위해서는 라이트와 머터리얼, 이 두 가지가 꼭 필요합니다.지난 편에서 회전하는 초록색 박스가 사실적으로 표현되지 않았는데요. 두 가지를 추가해서 우리에게 익숙한 화면으로 연출해 보겠습니다.라이팅에 대해 어디까지 다루어야 할지 고민이 되었습니다.보다 사실적으로 물체를 표현하기 위해서 아주 기본적인 물리적인 요소에서부터 점차 연산량과 고려 사항을 늘려나가 결국 쉐이터와 GPU를 탄생시키는데 일조했기 때문입니다.여기에서는 WebGL을 활용하여 기본적인 어플리케이션을 만드는데 필요한 수준까지만 설명하고, 이론적으로 알아두면 좋은 것 같은 부분은 문서 마지막에 레퍼런스로 소개하도록 하겠습니다.기본적으로 라이팅의 종류를 설명할 때는 광원(Light source)으로 분류합니다. 광원은 빛을 방출하는 지점 또는 영역을 의미합니다.다양한 종류의 광원이 있으며, 각각 고유의 특성을 가지고 있습니다.• None 방향광(Directional Light): 빛이 일정한 방향으로 평행하게 쏟아지는 광원으로, 태양광과 비슷합니다. 아주 먼 곳에서 다량의 광선이 쏟아져 온다고 가정하는 것으로, 빛의 방향은 모두 평행합니다. 방향광이 중요한 이유는 우리 실생활에서 느끼는 자연광과 매우 유사한 결과를 만들어주며, 마찬가지로 직관적으로 이해할 수 있는 그림자를 생성하기 때문입니다.• None 점광원(Point Light): 특정 지점에서 모든 방향으로 빛을 방출하는 광원입니다. 전구와 같은 방식으로 빛이 퍼집니다. 빛의 세기는 광원으로부터 거리와 비례합니다. 다음의 예제를 통해 쉽게 이해할 수 있습니다.• None 스포트라이트(Spotlight): 특정 방향으로만 빛이 집중되도록 방출되는 광원입니다. 손전등처럼 빛의 범위가 제한됩니다. 광원으로부터 콘(cone) 모양으로 방출됩니다.• None 환경광(Ambient Light): 장면 전체에 고르게 퍼진 빛으로, 특정한 광원에서 오는 것이 아닌 전체적인 분위기를 조성하는 데 사용됩니다. 모든 물체의 표면에 동일하게 작용합니다.하나의 장면(scene)에 하나의 조명(light)만 사용하는 것은 아닙니다.연출하고자 하는 장면에 따라 여러개의 다양한 조명을 쓰기도 하는데요.지난 시간에 사용했던 예제에 다음의 코드를 추가해 주겠습니다.DirectionalLight의 파라미터는 빛의 색상과 강도입니다. 이후에 3차원 좌표로 위치를 설정해줬고요.무엇이 바뀌었나요? 아무것도 바뀌지 않았습니다. 이제 머티리얼을 배울 차례입니다.머티리얼(Material; 재질)은 어떠한 물체의 외관을 묘사하는 다양한 속성들을 말합니다.금속은 빛을 잘 반사하기 때문에 반짝거리는 특성이 있습니다. 반면 마른 흙이나 고무 등은 금속보다 빛을 덜 반사합니다.옷감 중에서도 면과 벨벳은 명확하게 구분되는 시각적 차이를 가지고 있습니다.컴퓨터 그래픽스는 물체의 색상, 질감, 형태, 빛의 반사 등의 다양한 특성을 조합하여 시각적으로 표현하고자 하며, 이러한 특성들의 조합을 머티리얼이라고 지칭하는 것입니다.Three.js 라이브러리에서 자주 사용하는 대표적인 몇 가지 Material을 다루어 보겠습니다.아래로 갈수록 더 많은 계산 비용이 필요하며, 더 사실적으로 표현할 수 있습니다.반사율을 계산하기 위해 Lamberial 모델을 사용합니다.이 모델은 광택(specular highlights)에 대한 고려가 되어 있지 않기 때문에 나무나 돌과 같은 표면은 잘 표현할 수 있지만, 금속이나 반짝이는 재질을 표현하기는 어렵습니다.하지만 빠르게 동작하고 고유의 색을 표현하는데에는 부족함이 없기 때문에 활용할 부분이 많아요.비물리 기반의 Blinn-Phong 모델을 사용합니다. Lambertian 모델과는 달리 반사되는 광택을 더 잘 표현할 수 있습니다.Unity, Unreal 등에서 자주 사용하고 3D Max, Maya등의 그래픽스 스탠다드 도구에서도 프로 레벨에서 많이 사용하는 모델입니다.물리 기반 렌더링(PBR; Physically based rendering) 기법의 일부를 사용합니다. 물리 기반 렌더링이란 빛을 물리적, 수치적으로 모델링하여 정확하게 계산해보자는 접근 방법입니다.다른 기법들보다 더 사실적이고 정확한 표현이 가능하지만 계산량이 아주 많다는 단점이 있습니다.예전에는 영화 쪽에서나 사용할 수 있던 기법인데 최근 GPU의 등장으로 실시간 계산 성능이 좋아지면서 게임이나 Web 쪽으로 적용되고 있는 추세입니다.Standard material의 확장판으로, 더 다양한 속성의 물리 기반 렌더링을 지원합니다. 더 많은 계산 비용을 사용하지만, 활용하기에 따라서 더 사실적인 결과를 연출할 수 있습니다.머티리얼 몇 개를 사용해보고 차이점을 눈으로 확인해보도록 하지요. 우리의 cube를 MeshLambertMaterial로 바꾸어 보겠습니다.실행해보니 뭔가 이상합니다. 이제 큐브가 3차원인 느낌이 나는 것 같긴 한데 자꾸 중간에 사라집니다.이유는 이렇습니다. 우리는 윗쪽에 directional light를 설정했고, 그 조명의 빛을 받는 면만 화면에 그려지고 있는 것입니다.실생활에서는 조명과 떨어져 있더라도 환경에 의해 반사되는 빛들로 다른 부분도 표현이 되지만, 그래픽스의 세상에서는 반사되는 빛을 표현하려면 이것 역시 계산해 줘야 합니다.그렇다면 간단하게 극복할 수 있는 방법은 무엇일까요? 위에서 언급한 ambient light를 이럴 때 사용합니다.이 공간에는 “기본적으로 반사되는 빛이 있어”라고 설정해버리는 것이죠. Directional light 다음에 아래의 코드를 추가합니다.AmbientLight의 파라미터는 색상 값과 빛의 강도입니다.이렇게 설정하는게 찜찜할 수도 있습니다. 뭔가 정확한 느낌은 아니잖아요?그러나 계산량을 줄이면서 그럴싸해 보이
8/1/2025

WebGL과 함께 배우는 컴퓨터 그래픽스 (2)
얼마 전에 충격적인 사실을 알았습니다. 지난 기고에 참고했던 문서가 번역본을 제공하더라고요.알고보니 제가 애써서 번역할 필요가 없었습니다. 코드를 이해하는데 꼭 필요한 기반 지식을 설명하는데 큰 의의가 있기는 했지만요.이번에는 라이팅(Lighting)와 머티리얼(Material)에 대해 다루려고 합니다.컴퓨터 그래픽스에서 어떤 물체를 사실적으로 표현하기 위해서는 라이트와 머터리얼, 이 두 가지가 꼭 필요합니다.지난 편에서 회전하는 초록색 박스가 사실적으로 표현되지 않았는데요. 두 가지를 추가해서 우리에게 익숙한 화면으로 연출해 보겠습니다.라이팅에 대해 어디까지 다루어야 할지 고민이 되었습니다.보다 사실적으로 물체를 표현하기 위해서 아주 기본적인 물리적인 요소에서부터 점차 연산량과 고려 사항을 늘려나가 결국 쉐이터와 GPU를 탄생시키는데 일조했기 때문입니다.여기에서는 WebGL을 활용하여 기본적인 어플리케이션을 만드는데 필요한 수준까지만 설명하고, 이론적으로 알아두면 좋은 것 같은 부분은 문서 마지막에 레퍼런스로 소개하도록 하겠습니다.기본적으로 라이팅의 종류를 설명할 때는 광원(Light source)으로 분류합니다. 광원은 빛을 방출하는 지점 또는 영역을 의미합니다.다양한 종류의 광원이 있으며, 각각 고유의 특성을 가지고 있습니다.• None 방향광(Directional Light): 빛이 일정한 방향으로 평행하게 쏟아지는 광원으로, 태양광과 비슷합니다. 아주 먼 곳에서 다량의 광선이 쏟아져 온다고 가정하는 것으로, 빛의 방향은 모두 평행합니다. 방향광이 중요한 이유는 우리 실생활에서 느끼는 자연광과 매우 유사한 결과를 만들어주며, 마찬가지로 직관적으로 이해할 수 있는 그림자를 생성하기 때문입니다.• None 점광원(Point Light): 특정 지점에서 모든 방향으로 빛을 방출하는 광원입니다. 전구와 같은 방식으로 빛이 퍼집니다. 빛의 세기는 광원으로부터 거리와 비례합니다. 다음의 예제를 통해 쉽게 이해할 수 있습니다.• None 스포트라이트(Spotlight): 특정 방향으로만 빛이 집중되도록 방출되는 광원입니다. 손전등처럼 빛의 범위가 제한됩니다. 광원으로부터 콘(cone) 모양으로 방출됩니다.• None 환경광(Ambient Light): 장면 전체에 고르게 퍼진 빛으로, 특정한 광원에서 오는 것이 아닌 전체적인 분위기를 조성하는 데 사용됩니다. 모든 물체의 표면에 동일하게 작용합니다.하나의 장면(scene)에 하나의 조명(light)만 사용하는 것은 아닙니다.연출하고자 하는 장면에 따라 여러개의 다양한 조명을 쓰기도 하는데요.지난 시간에 사용했던 예제에 다음의 코드를 추가해 주겠습니다.DirectionalLight의 파라미터는 빛의 색상과 강도입니다. 이후에 3차원 좌표로 위치를 설정해줬고요.무엇이 바뀌었나요? 아무것도 바뀌지 않았습니다. 이제 머티리얼을 배울 차례입니다.머티리얼(Material; 재질)은 어떠한 물체의 외관을 묘사하는 다양한 속성들을 말합니다.금속은 빛을 잘 반사하기 때문에 반짝거리는 특성이 있습니다. 반면 마른 흙이나 고무 등은 금속보다 빛을 덜 반사합니다.옷감 중에서도 면과 벨벳은 명확하게 구분되는 시각적 차이를 가지고 있습니다.컴퓨터 그래픽스는 물체의 색상, 질감, 형태, 빛의 반사 등의 다양한 특성을 조합하여 시각적으로 표현하고자 하며, 이러한 특성들의 조합을 머티리얼이라고 지칭하는 것입니다.Three.js 라이브러리에서 자주 사용하는 대표적인 몇 가지 Material을 다루어 보겠습니다.아래로 갈수록 더 많은 계산 비용이 필요하며, 더 사실적으로 표현할 수 있습니다.반사율을 계산하기 위해 Lamberial 모델을 사용합니다.이 모델은 광택(specular highlights)에 대한 고려가 되어 있지 않기 때문에 나무나 돌과 같은 표면은 잘 표현할 수 있지만, 금속이나 반짝이는 재질을 표현하기는 어렵습니다.하지만 빠르게 동작하고 고유의 색을 표현하는데에는 부족함이 없기 때문에 활용할 부분이 많아요.비물리 기반의 Blinn-Phong 모델을 사용합니다. Lambertian 모델과는 달리 반사되는 광택을 더 잘 표현할 수 있습니다.Unity, Unreal 등에서 자주 사용하고 3D Max, Maya등의 그래픽스 스탠다드 도구에서도 프로 레벨에서 많이 사용하는 모델입니다.물리 기반 렌더링(PBR; Physically based rendering) 기법의 일부를 사용합니다. 물리 기반 렌더링이란 빛을 물리적, 수치적으로 모델링하여 정확하게 계산해보자는 접근 방법입니다.다른 기법들보다 더 사실적이고 정확한 표현이 가능하지만 계산량이 아주 많다는 단점이 있습니다.예전에는 영화 쪽에서나 사용할 수 있던 기법인데 최근 GPU의 등장으로 실시간 계산 성능이 좋아지면서 게임이나 Web 쪽으로 적용되고 있는 추세입니다.Standard material의 확장판으로, 더 다양한 속성의 물리 기반 렌더링을 지원합니다. 더 많은 계산 비용을 사용하지만, 활용하기에 따라서 더 사실적인 결과를 연출할 수 있습니다.머티리얼 몇 개를 사용해보고 차이점을 눈으로 확인해보도록 하지요. 우리의 cube를 MeshLambertMaterial로 바꾸어 보겠습니다.실행해보니 뭔가 이상합니다. 이제 큐브가 3차원인 느낌이 나는 것 같긴 한데 자꾸 중간에 사라집니다.이유는 이렇습니다. 우리는 윗쪽에 directional light를 설정했고, 그 조명의 빛을 받는 면만 화면에 그려지고 있는 것입니다.실생활에서는 조명과 떨어져 있더라도 환경에 의해 반사되는 빛들로 다른 부분도 표현이 되지만, 그래픽스의 세상에서는 반사되는 빛을 표현하려면 이것 역시 계산해 줘야 합니다.그렇다면 간단하게 극복할 수 있는 방법은 무엇일까요? 위에서 언급한 ambient light를 이럴 때 사용합니다.이 공간에는 “기본적으로 반사되는 빛이 있어”라고 설정해버리는 것이죠. Directional light 다음에 아래의 코드를 추가합니다.AmbientLight의 파라미터는 색상 값과 빛의 강도입니다.이렇게 설정하는게 찜찜할 수도 있습니다. 뭔가 정확한 느낌은 아니잖아요?그러나 계산량을 줄이면서 그럴싸해 보이
2025.08.01

좋아요

별로에요

AWS MediaConvert 를 활용한 동영상 스트리밍 서비스 구축기 - 2편
1편에 이어서 2편은 IAM 나머지 설정(임시 자격 증명)과 S3 버킷 설정을 진행해 보겠습니다.1편은 아래를 참고해 주세요.장기 자격 증명과는 반대되는 개념.먼저 장기 자격 증명은 보통 app 등에서 aws 리소스 접근시 aws 유저의 acesskey, secretkey를 입력해서 접근하게 되는데 이 acesskey, secretkey 가 유출(github등) 되면 제한없이 aws 리소스 접근이 되기 때문에 보안에 취약하다.임시 자격 증명은 단기로 사용할 수 있는 자격 증명이다.몇분에서 몇시간 까지 지속시간이 정해져 있어서 '임시' 라는 말이 들어간다. 자격 증명이 만료된 이후에는 어떤 종류의 액세스도 허용되지 않는다.장기 자격 증명 보다는 다소 번거로운 작업들이 있지만 대신 보안에 뛰어나다.IAM User(깡통)가 특정 Role의 모자를 써서 마치 권한이 있는 User처럼 되는걸 AssumeRole 이라고 한다.클라이언트(app등)에서 프로그래밍 방식으로 접근할때도 위 Flow대로 개발하여 접근해야 한다.AWS에 접근이 가능하지만, 아무런 권한이 없는 깡통 유저IAM User가 역할로부터 임시 토큰을 얻기 위한 시스템.특정 AWS 리소스 권한을 가지고 있는 역할(S3 라던지 Lambda 등등)아무권한 없는 aws 유저를 먼저 생성한다.(아무것도 설정 안하고 계속 다음 다음... )사용자 생성후에 액세스키를 만든다(클라이언트에서 접속하기 위한)"액세스 키 만들기" 클릭하여 생성 → accesskey / secretkey를 잘 보관하여 사용하는 클라이언트에게 전달만든 유저 클릭 → 권한 추가 → 인라인 정책 생성신뢰할 수 있는 엔티티 유형 → AWS 계정 → 이 계정 선택ARN 확인(ARN 정보를 클라이언트에게 전달해야 한다)임시권한 획득을 위해 클라이언트에게 제공할 데이터버킷 이름은 {환경}-ifland-s3-mediaconvert 로 생성• None 임의의 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단• None 새 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단• None 임의의 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 및 교차 계정 액세스 차단 "새 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단" 까지 체크시, mediaConvert에서 S3 접근시 Access denied 오류가 발생. 버킷 정책에 Cloud Front 생성시 같이 생성한 OAC 정책을 복붙해줌.위 설정을 하지 않으면 mediaConvert에서 S3 접근시 ACL 오류 발생.CORS(Cross-Origin 리소스 공유) 섹션에서 편집을 선택.CORS 구성 텍스트 상자에서 아래 CORS 구성을 복사하여 붙여 넣는다.바쁘다는 핑계로 2편이 좀 늦어졌네요.다음 연재는 더 빠르게 할수 있도록 노력하겠습니다!!!
8/1/2025

AWS MediaConvert 를 활용한 동영상 스트리밍 서비스 구축기 - 2편
1편에 이어서 2편은 IAM 나머지 설정(임시 자격 증명)과 S3 버킷 설정을 진행해 보겠습니다.1편은 아래를 참고해 주세요.장기 자격 증명과는 반대되는 개념.먼저 장기 자격 증명은 보통 app 등에서 aws 리소스 접근시 aws 유저의 acesskey, secretkey를 입력해서 접근하게 되는데 이 acesskey, secretkey 가 유출(github등) 되면 제한없이 aws 리소스 접근이 되기 때문에 보안에 취약하다.임시 자격 증명은 단기로 사용할 수 있는 자격 증명이다.몇분에서 몇시간 까지 지속시간이 정해져 있어서 '임시' 라는 말이 들어간다. 자격 증명이 만료된 이후에는 어떤 종류의 액세스도 허용되지 않는다.장기 자격 증명 보다는 다소 번거로운 작업들이 있지만 대신 보안에 뛰어나다.IAM User(깡통)가 특정 Role의 모자를 써서 마치 권한이 있는 User처럼 되는걸 AssumeRole 이라고 한다.클라이언트(app등)에서 프로그래밍 방식으로 접근할때도 위 Flow대로 개발하여 접근해야 한다.AWS에 접근이 가능하지만, 아무런 권한이 없는 깡통 유저IAM User가 역할로부터 임시 토큰을 얻기 위한 시스템.특정 AWS 리소스 권한을 가지고 있는 역할(S3 라던지 Lambda 등등)아무권한 없는 aws 유저를 먼저 생성한다.(아무것도 설정 안하고 계속 다음 다음... )사용자 생성후에 액세스키를 만든다(클라이언트에서 접속하기 위한)"액세스 키 만들기" 클릭하여 생성 → accesskey / secretkey를 잘 보관하여 사용하는 클라이언트에게 전달만든 유저 클릭 → 권한 추가 → 인라인 정책 생성신뢰할 수 있는 엔티티 유형 → AWS 계정 → 이 계정 선택ARN 확인(ARN 정보를 클라이언트에게 전달해야 한다)임시권한 획득을 위해 클라이언트에게 제공할 데이터버킷 이름은 {환경}-ifland-s3-mediaconvert 로 생성• None 임의의 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단• None 새 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단• None 임의의 퍼블릭 버킷 또는 액세스 지점 정책을 통해 부여된 버킷 및 객체에 대한 퍼블릭 및 교차 계정 액세스 차단 "새 ACL(액세스 제어 목록)을 통해 부여된 버킷 및 객체에 대한 퍼블릭 액세스 차단" 까지 체크시, mediaConvert에서 S3 접근시 Access denied 오류가 발생. 버킷 정책에 Cloud Front 생성시 같이 생성한 OAC 정책을 복붙해줌.위 설정을 하지 않으면 mediaConvert에서 S3 접근시 ACL 오류 발생.CORS(Cross-Origin 리소스 공유) 섹션에서 편집을 선택.CORS 구성 텍스트 상자에서 아래 CORS 구성을 복사하여 붙여 넣는다.바쁘다는 핑계로 2편이 좀 늦어졌네요.다음 연재는 더 빠르게 할수 있도록 노력하겠습니다!!!
2025.08.01

좋아요

별로에요

일 평균 30억 건을 처리하는 결제 시스템의 DB를 Vitess로 교체하기 - 2. 개발 및 운영기
안녕하세요. LINE Billing Platform 개발 팀의 김영재, 이정재입니다. LINE Billing Platform 개발 팀에서는 LINE 앱 내 여러 서비스에서 사용하는 결제, 포인트 플랫폼을 이용한 결제 기능을 제공하고 있습니다.저희 팀은 최근 가장 핵심 시스템이자 오랫동안 운영해 온 결제 시스템의 DB를 Nbase-T에서 Vitess로 마이그레이션했습니다. 이와 관련해서 앞서 일 평균 30억 건을 처리하는 결제 시스템의 DB를 Vitess로 교체하기 - 1. 솔루션 선정기라는 글에서 마이그레이션할 다음 솔루션을 선정하기 위해 진행한 PoC 과정과 그 결과 Vitess를 선정한 이유를 소개했는데요. 이번 글에서는 실제로 개발 및 운영 단계에서 Vitess를 어떻게 활용하고 있는지 소개하겠습니다(1편에서 Vitess에 대해 자세히 소개하고 있으므로 1편을 먼저 읽고 오시면 2편을 보다 쉽게 이해하실 수 있습니다).애플리케이션 서버에서 Vitess를 사용하려면 먼저 프로토콜을 선택하고 그에 맞춰 개발해야 합니다. 개발기에서는 저희 팀이 현재 어떤 프로토콜을 사용하고 있는지 소개하고, 아울러 Vitess의 MySQL 호환성과 함께 Vitess가 제공하는 다양한 유용한 기능들도 함께 살펴보겠습니다.Vitess 사용 준비가 완료되어 VTGate에 DB 툴(예: MySQL Workbench)로 접속해 쿼리를 실행할 수 있다면, 이제 애플리케이션 서버에서 어떤 프로토콜을 사용할지 선택할 차례입니다.VTGate는 MySQL 프로토콜과 gRPC 프로토콜을 지원합니다. 따라서 어떤 프로토콜로 개발할지 선택해야 했는데요. 저희는 과거 NBase-T 샤딩 DB에서 성능이 우수한 RPC 프로토콜을 사용한 경험이 있어서, 먼저 Vitess 공식 Git 리포지터리에서 제공하는 Java 클라이언트(참고) 중 grpc-client 모듈을 활용해 개발했습니다.그러나 개발 후 성능 테스트 과정에서 간헐적으로 에러가 발생했 고, CPU 사용량 또한 크게 증가하는 모습을 보였습니다. 또한 Vitess에서 제공하는 아래 예제 코드처럼 쿼리 결과를 Java 객체로 변환하는 과정이 매우 번거롭다는 문제도 있었습니다.저희는 Vitess 공식 Slack 채널에서 조사한 결과 gRPC 프로토콜 사용 시 CPU 오버헤드가 발생한다는 것을 알게 되었습니다. 또한 어떤 프로토콜을 사용해야 할지 문의한 결과 '현재 Vitess 측에서는 관련 모듈을 활발하게 개발하고 있지 않기 때문에 MySQL 프로토콜을 사용해 개발하는 것을 권장한다'는 답변을 받았습니다. 이에 따라 저희 팀은 MySQL 프로토콜을 활용해 애플리케이션을 개발했습니다.애플리케이션에서 Vitess를 이용해 데이터를 처리하는 방식현재 저희 팀이 구성한 키스페이스와 스키마를 공유하고, 이를 바탕으로 실제 애플리케이션에서 요청을 처리하는 과정을 설명하겠습니다.저희 팀은 글로벌 키스페이스(global keyspace)와 서비스 키스페이스(service keyspace), 이렇게 두 개의 키스페이스를 운영하고 있습니다.먼저 글로벌 키스페이스는 단일 샤드로, 자동 증가하는 샤딩 키 관리 테이블을 생성해서 사용하고 있습니다. 이 테이블에는 사용자 정보와 샤딩 키값이 저장됩니다. 대략적인 스키마와 Vitess 스키마(이하 VSchema)는 다음과 같습니다.다음으로 서비스 키스페이스는 N개의 샤드로 분산해 운영하고 있으며, 가상 재화 서비스를 제공하기 위한 코인 잔액과 코인 충전, 사용 내역 등의 데이터를 저장하는 테이블을 운영하고 있습니다. 서비스 키스페이스는 프라이머리-레플리카-읽기전용(primay-replica-readonly) 혹은 프라이머리-레플리카(primary-replica) 구조 등으로 운영할 수 있습니다.다음은 저희가 사용하는 사용 내역 테이블의 대략적인 스키마와 VSchema입니다. Vindex는 해시를 사용하고 있습니다.애플리케이션이 Vitess와 작동하는 방식다음은 애플리케이션에서 코인 충전 요청이 들어왔을 때 애플리케이션과 Vitess의 작동 흐름을 나타낸 것입니다.위 그림처럼 샤딩 키를 조건절에 넣거나 데이터 삽입( ) 시 샤딩 키를 포함해 삽입하면 VTGate에서 해당 사용자의 데이터가 존재하는 샤드를 특정하며, 이를 통해 해당 샤드에만 쿼리가 수행됩니다.MySQL과의 호환성Vitess의 트랜잭션 격리 수준과 쿼리 제약 사항 등 MySQL 호환성에 대해 소개하겠습니다.Vitess는 단일 샤드 트랜잭션에서는 MySQL의 트랜잭션 격리 수준(isolation level) 중 가 적용되며, 다중 샤드 트랜잭션에서는 가 적용됩니다. 클라이언트는 명령을 활용해 트랜잭션 모드를 변경할 수 있습니다.Vitess가 MySQL 프로토콜을 지원하기는 하지만 샤딩 DB 특성에 따라 일부 쿼리에 제약 사항이 존재합니다. 쿼리 제약 사항은 Vitess의 공식 Git 리포지터리의 unsupported_cases.json에서 관리되고 있습니다.• Views: 샤딩된 키스페이스에 대해서만 지원하며, VTGate와 VTTablet 실행 시 옵션을 활성화하면 사용할 수 있습니다.• 임시 테이블: 샤딩되지 않은 키스페이스에서만 생성할 수 있습니다.이 글에서 소개한 기능 외에 MySQL 기능 호환 여부는 Vitess에서 공식으로 제공하는 MySQL Compatibility 문서를 참고하시기 바랍니다.Vitess가 제공하는 여러 유용한 기능(참고) 중에서 현재 저희 팀이 사용하고 있거나 추후 사용을 고려하고 있는 기능들을 소개하겠습니다.• VEXPLAIN, VTEXPLAIN: 쿼리 실행 계획을 분석하는 데 사용합니다. VTEXPLAIN 기능의 경우 VTAdmin에서 웹 UI로 수행할 수 있기 때문에 저희는 VTAdmin을 이용해 실행 계획을 분석하고 있습니다.다음으로 운영 단계에서 Vitess를 어떻게 활용하고 있는지 모니터링 방식과 DB 운영 프로세스, 페일오버(failover) 테스트로 나눠 하나씩 살펴보겠습니다.저희는 현재 주로 VTOrc 지표(metrics)와 VTGate 및 VTTablet 지표, Vitess 로그 등을 모니터링하고 있습니다. 각각을 어떻게 모니터링
java
mysql
nodejs
8/1/2025

일 평균 30억 건을 처리하는 결제 시스템의 DB를 Vitess로 교체하기 - 2. 개발 및 운영기
안녕하세요. LINE Billing Platform 개발 팀의 김영재, 이정재입니다. LINE Billing Platform 개발 팀에서는 LINE 앱 내 여러 서비스에서 사용하는 결제, 포인트 플랫폼을 이용한 결제 기능을 제공하고 있습니다.저희 팀은 최근 가장 핵심 시스템이자 오랫동안 운영해 온 결제 시스템의 DB를 Nbase-T에서 Vitess로 마이그레이션했습니다. 이와 관련해서 앞서 일 평균 30억 건을 처리하는 결제 시스템의 DB를 Vitess로 교체하기 - 1. 솔루션 선정기라는 글에서 마이그레이션할 다음 솔루션을 선정하기 위해 진행한 PoC 과정과 그 결과 Vitess를 선정한 이유를 소개했는데요. 이번 글에서는 실제로 개발 및 운영 단계에서 Vitess를 어떻게 활용하고 있는지 소개하겠습니다(1편에서 Vitess에 대해 자세히 소개하고 있으므로 1편을 먼저 읽고 오시면 2편을 보다 쉽게 이해하실 수 있습니다).애플리케이션 서버에서 Vitess를 사용하려면 먼저 프로토콜을 선택하고 그에 맞춰 개발해야 합니다. 개발기에서는 저희 팀이 현재 어떤 프로토콜을 사용하고 있는지 소개하고, 아울러 Vitess의 MySQL 호환성과 함께 Vitess가 제공하는 다양한 유용한 기능들도 함께 살펴보겠습니다.Vitess 사용 준비가 완료되어 VTGate에 DB 툴(예: MySQL Workbench)로 접속해 쿼리를 실행할 수 있다면, 이제 애플리케이션 서버에서 어떤 프로토콜을 사용할지 선택할 차례입니다.VTGate는 MySQL 프로토콜과 gRPC 프로토콜을 지원합니다. 따라서 어떤 프로토콜로 개발할지 선택해야 했는데요. 저희는 과거 NBase-T 샤딩 DB에서 성능이 우수한 RPC 프로토콜을 사용한 경험이 있어서, 먼저 Vitess 공식 Git 리포지터리에서 제공하는 Java 클라이언트(참고) 중 grpc-client 모듈을 활용해 개발했습니다.그러나 개발 후 성능 테스트 과정에서 간헐적으로 에러가 발생했 고, CPU 사용량 또한 크게 증가하는 모습을 보였습니다. 또한 Vitess에서 제공하는 아래 예제 코드처럼 쿼리 결과를 Java 객체로 변환하는 과정이 매우 번거롭다는 문제도 있었습니다.저희는 Vitess 공식 Slack 채널에서 조사한 결과 gRPC 프로토콜 사용 시 CPU 오버헤드가 발생한다는 것을 알게 되었습니다. 또한 어떤 프로토콜을 사용해야 할지 문의한 결과 '현재 Vitess 측에서는 관련 모듈을 활발하게 개발하고 있지 않기 때문에 MySQL 프로토콜을 사용해 개발하는 것을 권장한다'는 답변을 받았습니다. 이에 따라 저희 팀은 MySQL 프로토콜을 활용해 애플리케이션을 개발했습니다.애플리케이션에서 Vitess를 이용해 데이터를 처리하는 방식현재 저희 팀이 구성한 키스페이스와 스키마를 공유하고, 이를 바탕으로 실제 애플리케이션에서 요청을 처리하는 과정을 설명하겠습니다.저희 팀은 글로벌 키스페이스(global keyspace)와 서비스 키스페이스(service keyspace), 이렇게 두 개의 키스페이스를 운영하고 있습니다.먼저 글로벌 키스페이스는 단일 샤드로, 자동 증가하는 샤딩 키 관리 테이블을 생성해서 사용하고 있습니다. 이 테이블에는 사용자 정보와 샤딩 키값이 저장됩니다. 대략적인 스키마와 Vitess 스키마(이하 VSchema)는 다음과 같습니다.다음으로 서비스 키스페이스는 N개의 샤드로 분산해 운영하고 있으며, 가상 재화 서비스를 제공하기 위한 코인 잔액과 코인 충전, 사용 내역 등의 데이터를 저장하는 테이블을 운영하고 있습니다. 서비스 키스페이스는 프라이머리-레플리카-읽기전용(primay-replica-readonly) 혹은 프라이머리-레플리카(primary-replica) 구조 등으로 운영할 수 있습니다.다음은 저희가 사용하는 사용 내역 테이블의 대략적인 스키마와 VSchema입니다. Vindex는 해시를 사용하고 있습니다.애플리케이션이 Vitess와 작동하는 방식다음은 애플리케이션에서 코인 충전 요청이 들어왔을 때 애플리케이션과 Vitess의 작동 흐름을 나타낸 것입니다.위 그림처럼 샤딩 키를 조건절에 넣거나 데이터 삽입( ) 시 샤딩 키를 포함해 삽입하면 VTGate에서 해당 사용자의 데이터가 존재하는 샤드를 특정하며, 이를 통해 해당 샤드에만 쿼리가 수행됩니다.MySQL과의 호환성Vitess의 트랜잭션 격리 수준과 쿼리 제약 사항 등 MySQL 호환성에 대해 소개하겠습니다.Vitess는 단일 샤드 트랜잭션에서는 MySQL의 트랜잭션 격리 수준(isolation level) 중 가 적용되며, 다중 샤드 트랜잭션에서는 가 적용됩니다. 클라이언트는 명령을 활용해 트랜잭션 모드를 변경할 수 있습니다.Vitess가 MySQL 프로토콜을 지원하기는 하지만 샤딩 DB 특성에 따라 일부 쿼리에 제약 사항이 존재합니다. 쿼리 제약 사항은 Vitess의 공식 Git 리포지터리의 unsupported_cases.json에서 관리되고 있습니다.• Views: 샤딩된 키스페이스에 대해서만 지원하며, VTGate와 VTTablet 실행 시 옵션을 활성화하면 사용할 수 있습니다.• 임시 테이블: 샤딩되지 않은 키스페이스에서만 생성할 수 있습니다.이 글에서 소개한 기능 외에 MySQL 기능 호환 여부는 Vitess에서 공식으로 제공하는 MySQL Compatibility 문서를 참고하시기 바랍니다.Vitess가 제공하는 여러 유용한 기능(참고) 중에서 현재 저희 팀이 사용하고 있거나 추후 사용을 고려하고 있는 기능들을 소개하겠습니다.• VEXPLAIN, VTEXPLAIN: 쿼리 실행 계획을 분석하는 데 사용합니다. VTEXPLAIN 기능의 경우 VTAdmin에서 웹 UI로 수행할 수 있기 때문에 저희는 VTAdmin을 이용해 실행 계획을 분석하고 있습니다.다음으로 운영 단계에서 Vitess를 어떻게 활용하고 있는지 모니터링 방식과 DB 운영 프로세스, 페일오버(failover) 테스트로 나눠 하나씩 살펴보겠습니다.저희는 현재 주로 VTOrc 지표(metrics)와 VTGate 및 VTTablet 지표, Vitess 로그 등을 모니터링하고 있습니다. 각각을 어떻게 모니터링
2025.08.01
java
mysql
nodejs

좋아요

별로에요

실시간 유효 광고 선정을 위한 Flink에서 Apache Paimon 도입기
저희 팀에서는 Apache Spark를 기반으로 배치 및 실시간 처리를 수행하는 AI 데이터 파이프라인을 구축하고 있습니다. 그 과정에서 Spark의 마이크로 배치 아키텍처보다 더 효율적이고 생산적으로 실시간 데이터를 수집하고 집계하는 방법을 고민해왔습니다.이 글에서는 Apache Flink와 Apache Paimon을 도입하여 빠르고 안정적인 데이터 처리 파이프라인을 개발한 경험을 공유하고, 그 과정에 사용한 기술과 유용한 팁을 소개합니다.ADVoost Shopping에서 실시간으로 유효 광고를 선정하는 방법먼저 ADVoost Shopping 과제에서 실시간으로 유효 광고를 선정하기 위해 구축한 파이프라인에서 유효 광고 애셋 그룹 선정 파이프라인, 상품 광고 매핑 업데이트 파이프라인, 로그 수집 및 집계 파이프라인을 소개하겠습니다.Paimon을 활용한 실시간 유효 광고 선정 아키텍처AI Serving API가 광고를 빠르게 조회할 수 있도록, 실시간으로 유효 광고를 선정해 Feature Store에 적재하는 아키텍처입니다.유효 광고 애셋 그룹 선정가장 먼저, 유효 광고 선정에 필요한 메타성 테이블을 구성하기 위해, 광고 정보 CDC 데이터를 Kafka로부터 consume해서 Flink를 사용해 Paimon에 적재합니다. 이 과정에서 특정 테이블에는 Paimon의 부분 업데이트(partial update) 기능을 사용해 필요한 칼럼만 업데이트합니다.이렇게 Paimon에 데이터를 적재함으로써 Paimon의 다양한 기능을 활용할 수 있습니다. 그 중 몇 가지를 꼽아보자면, 스냅샷이나 태그, 변경 로그를 이용해 타임 트래블이 가능하고, Kafka에서는 쉽게 구현하기 힘든 데이터 간 조인이나 집계, 데이터 분석도 가능합니다.그 다음으로, 다양한 광고 정보를 조인하여 애셋 그룹 기준으로 반정규화된 데이터를 생성합니다. Paimon으로 반정규화된 데이터를 생성하면 부가 기능을 사용할 수 있습니다.이어서, 반정규화 데이터로부터 유효 광고를 선정해 유효 애셋 그룹 데이터를 생성합니다. Paimon의 rowkind 재정의 기능을 활용하면, 단순 SQL로 실행한 Flink 스트리밍 작업에서 데이터를 실시간으로 추가/수정/삭제할 수 있습니다.마지막으로, 이렇게 선정한 유효 애셋 그룹 데이터를 AI Serving API가 광고를 조회하는 Feature Store에 업로드합니다. 앞에서 추가/수정/삭제한 데이터는 당연히 Feature Store에서도 추가/수정/삭제됩니다.추가로, 유효 광고를 선정할 때는 일별 캠페인 소진량 데이터가 필요합니다. Flink는 한 번 처리한 데이터를 다시 처리하려면 데이터를 재발행해야 하는데, 일별 캠페인 소진량은 매일 초기화가 필요한 데이터입니다. 일별 배치 작업으로 캠페인 소진량 데이터를 초기화하기 위해서, 일별 초기화 결과 테이블과 소진량 테이블을 스트리밍 조인했습니다. 그 결과, 일별 초기화가 반영된 캠페인 소진량을 실시간으로 활용할 수 있게 되었습니다.상품 광고 매핑 정보 업데이트, 로그 수집과 집계상품 광고 매핑 정보 업데이트 파이프라인은 비교적 간단합니다. Kafka를 통해 상품과 연결된 애셋 그룹 매핑 정보를 CDC로 받아와서 Paimon에 추가/수정/삭제합니다. 이어서 Feature Store에도 동일한 스키마로 동기화해 상품과 애셋 그룹 매핑 Feature를 AI Serving API도 활용할 수 있도록 구축했습니다.그리고 로그 수집 및 집계 파이프라인도 비교적 간단합니다. 로그 데이터를 Kafka로부터 받아서 Paimon 테이블에 실시간으로 적재하고, 적재된 데이터를 활용해 Spark에서 배치로 집계하고, 집계 결과를 Hive와 Aerospike에 적재합니다. 집계 Feature도 Paimon으로 구축할 수 있지만, 이 작업에서는 기존에도 많이 사용하는 Hive를 그대로 활용했습니다.집계 데이터 기반 유효 광고 AI Serving앞선 파이프라인에서 Feature Store에 업로드된 데이터를 AI Serving에서는 어떻게 처리하는지 간단히 살펴보겠습니다.먼저, 광고 서버로부터 상품 정보 목록을 전달받습니다.다음 단계에서 애셋 그룹 매핑 Feature를 조회합니다. 최대 상품 개수인 200에 최대 애셋 그룹 매핑 개수인 500을 곱하면 최대 만 개의 유효 애셋 그룹이 필요합니다.세 번째로, 유효 애셋 그룹 Feature를 조회합니다. 이 유효 애셋 그룹 Feature를 사용해서, 최대 만 개의 매핑된 유효 애셋 그룹 중에서 실제로 광고를 송출할 수 있는 유효한 애셋 그룹을 선정합니다.그리고 유효 애셋 그룹에 해당하는 집계 Feature를 조회합니다. 이 집계 Feature를 활용해서 AI 모델에서 추론해 모델 결과 값을 생성합니다.그 결과, 상품별 모델 결과 값을 기준으로 광고 성과가 가장 최적화된 애셋 그룹을 하나씩 반환해 광고 송출에 사용합니다.실시간 유효 광고 선정에 Flink + Paimon을 도입한 이유Spark만 사용하는 방법으로도 파이프라인을 구축할 수 있었지만, 리소스를 효율적으로 사용하면서도 빠르고 안정적인 파이프라인을 구축하기 위해 Flink + Paimon 도입을 결정했습니다.Flink + Paimon 통합 사용성Flink는 실시간 처리 기능과 성능이 우수하고 레퍼런스도 풍부하며, Paimon은 태생이 Flink Table Store로 시작된 프로젝트인 만큼 Flink와 통합했을 때 사용성이 좋습니다. 또한 여기에 따라오는 다음과 같은 장점이 있습니다.실시간 집계(aggregation), 스키마 진화(schema evolution), 변경 로그(changelog) 지원부분 업데이트 기능 지원실시간 조인 실행 시 조인 키와 기본 키(primary key, 이하 PK)가 같으면 Flink에서 조인하는 대신 Paimon에 INSERT를 실행하는 것만으로 조인 처리 가능타임 트래블 지원Merge on Write 지원읽는 시점에 삭제 벡터(deletion vector)를 조합하여 읽기에 빠른 조회 가능쓰기 성능이 Merge on Read에 비해 느리지만, Copy on Write에 비해 빠름테이블 자동 관리 기능 지원자동 compa
flink
github
hadoop
hive
kafka
spark
7/31/2025

실시간 유효 광고 선정을 위한 Flink에서 Apache Paimon 도입기
저희 팀에서는 Apache Spark를 기반으로 배치 및 실시간 처리를 수행하는 AI 데이터 파이프라인을 구축하고 있습니다. 그 과정에서 Spark의 마이크로 배치 아키텍처보다 더 효율적이고 생산적으로 실시간 데이터를 수집하고 집계하는 방법을 고민해왔습니다.이 글에서는 Apache Flink와 Apache Paimon을 도입하여 빠르고 안정적인 데이터 처리 파이프라인을 개발한 경험을 공유하고, 그 과정에 사용한 기술과 유용한 팁을 소개합니다.ADVoost Shopping에서 실시간으로 유효 광고를 선정하는 방법먼저 ADVoost Shopping 과제에서 실시간으로 유효 광고를 선정하기 위해 구축한 파이프라인에서 유효 광고 애셋 그룹 선정 파이프라인, 상품 광고 매핑 업데이트 파이프라인, 로그 수집 및 집계 파이프라인을 소개하겠습니다.Paimon을 활용한 실시간 유효 광고 선정 아키텍처AI Serving API가 광고를 빠르게 조회할 수 있도록, 실시간으로 유효 광고를 선정해 Feature Store에 적재하는 아키텍처입니다.유효 광고 애셋 그룹 선정가장 먼저, 유효 광고 선정에 필요한 메타성 테이블을 구성하기 위해, 광고 정보 CDC 데이터를 Kafka로부터 consume해서 Flink를 사용해 Paimon에 적재합니다. 이 과정에서 특정 테이블에는 Paimon의 부분 업데이트(partial update) 기능을 사용해 필요한 칼럼만 업데이트합니다.이렇게 Paimon에 데이터를 적재함으로써 Paimon의 다양한 기능을 활용할 수 있습니다. 그 중 몇 가지를 꼽아보자면, 스냅샷이나 태그, 변경 로그를 이용해 타임 트래블이 가능하고, Kafka에서는 쉽게 구현하기 힘든 데이터 간 조인이나 집계, 데이터 분석도 가능합니다.그 다음으로, 다양한 광고 정보를 조인하여 애셋 그룹 기준으로 반정규화된 데이터를 생성합니다. Paimon으로 반정규화된 데이터를 생성하면 부가 기능을 사용할 수 있습니다.이어서, 반정규화 데이터로부터 유효 광고를 선정해 유효 애셋 그룹 데이터를 생성합니다. Paimon의 rowkind 재정의 기능을 활용하면, 단순 SQL로 실행한 Flink 스트리밍 작업에서 데이터를 실시간으로 추가/수정/삭제할 수 있습니다.마지막으로, 이렇게 선정한 유효 애셋 그룹 데이터를 AI Serving API가 광고를 조회하는 Feature Store에 업로드합니다. 앞에서 추가/수정/삭제한 데이터는 당연히 Feature Store에서도 추가/수정/삭제됩니다.추가로, 유효 광고를 선정할 때는 일별 캠페인 소진량 데이터가 필요합니다. Flink는 한 번 처리한 데이터를 다시 처리하려면 데이터를 재발행해야 하는데, 일별 캠페인 소진량은 매일 초기화가 필요한 데이터입니다. 일별 배치 작업으로 캠페인 소진량 데이터를 초기화하기 위해서, 일별 초기화 결과 테이블과 소진량 테이블을 스트리밍 조인했습니다. 그 결과, 일별 초기화가 반영된 캠페인 소진량을 실시간으로 활용할 수 있게 되었습니다.상품 광고 매핑 정보 업데이트, 로그 수집과 집계상품 광고 매핑 정보 업데이트 파이프라인은 비교적 간단합니다. Kafka를 통해 상품과 연결된 애셋 그룹 매핑 정보를 CDC로 받아와서 Paimon에 추가/수정/삭제합니다. 이어서 Feature Store에도 동일한 스키마로 동기화해 상품과 애셋 그룹 매핑 Feature를 AI Serving API도 활용할 수 있도록 구축했습니다.그리고 로그 수집 및 집계 파이프라인도 비교적 간단합니다. 로그 데이터를 Kafka로부터 받아서 Paimon 테이블에 실시간으로 적재하고, 적재된 데이터를 활용해 Spark에서 배치로 집계하고, 집계 결과를 Hive와 Aerospike에 적재합니다. 집계 Feature도 Paimon으로 구축할 수 있지만, 이 작업에서는 기존에도 많이 사용하는 Hive를 그대로 활용했습니다.집계 데이터 기반 유효 광고 AI Serving앞선 파이프라인에서 Feature Store에 업로드된 데이터를 AI Serving에서는 어떻게 처리하는지 간단히 살펴보겠습니다.먼저, 광고 서버로부터 상품 정보 목록을 전달받습니다.다음 단계에서 애셋 그룹 매핑 Feature를 조회합니다. 최대 상품 개수인 200에 최대 애셋 그룹 매핑 개수인 500을 곱하면 최대 만 개의 유효 애셋 그룹이 필요합니다.세 번째로, 유효 애셋 그룹 Feature를 조회합니다. 이 유효 애셋 그룹 Feature를 사용해서, 최대 만 개의 매핑된 유효 애셋 그룹 중에서 실제로 광고를 송출할 수 있는 유효한 애셋 그룹을 선정합니다.그리고 유효 애셋 그룹에 해당하는 집계 Feature를 조회합니다. 이 집계 Feature를 활용해서 AI 모델에서 추론해 모델 결과 값을 생성합니다.그 결과, 상품별 모델 결과 값을 기준으로 광고 성과가 가장 최적화된 애셋 그룹을 하나씩 반환해 광고 송출에 사용합니다.실시간 유효 광고 선정에 Flink + Paimon을 도입한 이유Spark만 사용하는 방법으로도 파이프라인을 구축할 수 있었지만, 리소스를 효율적으로 사용하면서도 빠르고 안정적인 파이프라인을 구축하기 위해 Flink + Paimon 도입을 결정했습니다.Flink + Paimon 통합 사용성Flink는 실시간 처리 기능과 성능이 우수하고 레퍼런스도 풍부하며, Paimon은 태생이 Flink Table Store로 시작된 프로젝트인 만큼 Flink와 통합했을 때 사용성이 좋습니다. 또한 여기에 따라오는 다음과 같은 장점이 있습니다.실시간 집계(aggregation), 스키마 진화(schema evolution), 변경 로그(changelog) 지원부분 업데이트 기능 지원실시간 조인 실행 시 조인 키와 기본 키(primary key, 이하 PK)가 같으면 Flink에서 조인하는 대신 Paimon에 INSERT를 실행하는 것만으로 조인 처리 가능타임 트래블 지원Merge on Write 지원읽는 시점에 삭제 벡터(deletion vector)를 조합하여 읽기에 빠른 조회 가능쓰기 성능이 Merge on Read에 비해 느리지만, Copy on Write에 비해 빠름테이블 자동 관리 기능 지원자동 compa
2025.07.31
flink
github
hadoop
hive
kafka
spark

좋아요

별로에요

앱 성능 측정 방법: 웹과 앱 지표 통합 분석 가이드
앱의 품질을 말할 때 기능성 외에 사용자들이 체감할 수 있는 가장 중요한 품질은 바로 반응 속도입니다.이는 앱의 비기능적 품질 속성 중에서도 사용자의 만족도에 직접적인 영향을 주는 요소입니다.‘반응 속도’라는 표현에는 앱 실행 속도, 페이지 로딩 속도, 콘텐츠 다운로드 시간, 화면 렌더링 속도 등 다양한 요소가 포함됩니다.이 글에서는 이러한 성능 품질에 대해 살펴보려 합니다.그럼 앱 성능 측정을 할 때 제일 먼저 고려해야 할 사항은 무엇일까요?테스트 대상, 환경, 범위, 테스트 시나리오, 품질 기준, 테스트 수행 방법 등은 모든 테스트의 기본 항목이며, 성능 테스트에서도 예외가 아닙니다.테스트 대상이 되는 앱이 네이티브 앱인지 하이브리드 앱인지에 따라 성능 측정 지표를 선택해야 합니다.웹뷰 기반 앱의 성능을 측정할 때는 앱 성능 지표(예: 앱 시작 속도, 메모리 사용량, 프레임 드랍율 등)과 함께 웹 성능 지표(예: FCP, LCP, CLS 등)도 함께 고려해야 정확한 품질 진단이 가능합니다.앱은 빠르게 실행되더라도, 웹뷰 내부의 웹 콘텐츠 로딩이 느릴 경우 사용자는 전체적으로 느린 앱으로 인식하게 됩니다.웹뷰 포함 여부와 관계없이 모든 플랫폼에서 공통으로 고려할 수 있는 성능 지표입니다.서버나 백엔드와의 상호작용을 포함하는 모든 앱에서 기본적으로 측정됩니다.• None 응답 시간 (Response Time): 사용자의 요청에 대해 시스템이 응답하는 데 걸리는 시간• None API 지연 시간 (API Latency): 백엔드 서버와의 통신 시간• None CPU / 메모리 사용량: 실행 중인 프로세스가 소비하는 시스템 자원• None Page Load Time (PL): 화면 또는 페이지가 요청된 후 첫 번째 UI가 완전히 표시될 때까지의 시간 (3초를 넘으면 사용자의 43%가 이탈하며, 5초가 넘으면 이탈률이 90%까지 증가한다고 합니다.)• None 사용자 인터랙션 반응 시간: 사용자가 탭, 스크롤, 입력 등의 행동을 했을 때, 앱이 이에 반응하는 속도. UI 반응이 느리면 기능이 잘 작동해도 “느린 앱”으로 인식.• None Tap Latency: 탭 입력 후 UI 반영까지 걸리는 시간. 0.1초 (100ms) 가 '순간적(instantaneous)'으로 인식되며 가장 이상적. 0.5~1초는 '즉각적(immediate)'으로, 1초를 넘기면 체감 반응 속도가 급격히 떨어짐.• None Scroll Responsiveness: 스크롤 시작 후 화면이 부드럽고 빠르게 따라오는 정도. 스크롤 중 프레임 드롭이 발생하거나 16ms(60fps) 기준을 반복적으로 벗어나면 버벅임으로 인식됨.네이티브 앱 또는 웹뷰 앱 전체를 기준으로 측정할 때는 아래와 같은 앱 전용 성능 지표를 고려해야 합니다.사용자가 앱 내에서 탭을 이동하거나 버튼을 눌렀을 때 새 화면이 얼마나 빠르게 나타나는지를 측정합니다.특히, 탭 UI, 스크롤 가능한 콘텐츠, 네트워크 기반 페이지 등은 체감 성능에 큰 영향을 줍니다.• None Navigation Time: 현재 화면에서 다른 화면(예: 탭 이동, 버튼 클릭 등)으로 전환되는 데 걸리는 시간. 네트워크/애니메이션 영향 포함.• None Fragment Render Time: 안드로이드에서 프래그먼트(Fragment)처럼 구분된 UI 컴포넌트가 생성되어 화면에 완전히 렌더링될 때까지의 시간. 복합적인 UI일 경우 이 시간이 길어짐.• None Screen Rendering Time: 한 화면이 렌더링 완료되는 데 걸리는 시간. 16ms (60fps) 초과 시 프레임 드롭 체감.• None 프레임 드랍 (FPS, Jank Rate): 화면 렌더링 중 애니메이션이 끊기는 현상. 초당 프레임 수(FPS)와 프레임 드랍률. FPS가 낮거나 Jank가 많으면 사용자 체감 품질 저하.• None 메모리 사용량 (Memory Usage): 앱이 사용하는 RAM의 크기.웹뷰(WebView)나 하이브리드 앱에서 웹 콘텐츠가 로드되는 성능은 아래와 같은 웹 전용 지표를 통해 측정할 수 있습니다.이 지표들은 주로 Google Lighthouse나 Web Vitals 기반 도구로 측정할 수 있습니다.• None FCP (First Contentful Paint): 화면에 첫 콘텐츠가 표시되기까지 걸리는 시간• None LCP (Largest Contentful Paint): 주요 콘텐츠(예: 이미지, 텍스트 블록)가 로드되는 시간• None TTI (Time to Interactive): 페이지가 실제로 상호작용 가능한 상태가 되기까지 걸리는 시간• None CLS (Cumulative Layout Shift): 페이지 로딩 중 요소가 예상치 못하게 이동하는 정도• None 페이지 로딩 시간: 전체 HTML 문서가 로딩되기까지 걸리는 시간• None LCP(Largest Contentful Paint): 가장 큰 콘텐츠가 화면에 뜨는 데 걸린 시간• None CLS (Cumulative Layout Shift): 페이지가 로딩되면서 화면이 흔들리는 정도• None INP (Interaction to Next Paint): 사용자 클릭 등 상호작용에 반응하기까지의 시간Android Vitals의 주요 항목 중 APP Launch Time 성능을 알아 보겠습니다.APP Launch Time은 크게 Cold, Warm, Hot Start 세 가지 상태로 분류됩니다.• None Cold Start (콜드 스타트): 처음 앱을 실행했을 때 초기 로딩 속도를 말합니다. 앱을 완전히 처음부터 실행하기 때문에 가장 무거운 시작 방식입니다. 시스템 프로세스 생성 → Application.onCreate() 실행 → 액티비티 생성 및 초기 화면 렌더링 등 모든 과정이 포함합니다. 여기서 최적화하면 나머지 상태도 전반적으로 빠르게 개선될 수 있습니다. 그만큼 개선하기 어렵다는 뜻이기도 합니다.• None Warm Start (웜 스타트): 백그라운드에 있던 앱을 다시 열었을 때의 로딩 속도를 말합니다. 이미 앱 프로세스는 살아있으나, 새로운 액티비티 생성이 필요한 경우로 Cold Start
7/31/2025

앱 성능 측정 방법: 웹과 앱 지표 통합 분석 가이드
앱의 품질을 말할 때 기능성 외에 사용자들이 체감할 수 있는 가장 중요한 품질은 바로 반응 속도입니다.이는 앱의 비기능적 품질 속성 중에서도 사용자의 만족도에 직접적인 영향을 주는 요소입니다.‘반응 속도’라는 표현에는 앱 실행 속도, 페이지 로딩 속도, 콘텐츠 다운로드 시간, 화면 렌더링 속도 등 다양한 요소가 포함됩니다.이 글에서는 이러한 성능 품질에 대해 살펴보려 합니다.그럼 앱 성능 측정을 할 때 제일 먼저 고려해야 할 사항은 무엇일까요?테스트 대상, 환경, 범위, 테스트 시나리오, 품질 기준, 테스트 수행 방법 등은 모든 테스트의 기본 항목이며, 성능 테스트에서도 예외가 아닙니다.테스트 대상이 되는 앱이 네이티브 앱인지 하이브리드 앱인지에 따라 성능 측정 지표를 선택해야 합니다.웹뷰 기반 앱의 성능을 측정할 때는 앱 성능 지표(예: 앱 시작 속도, 메모리 사용량, 프레임 드랍율 등)과 함께 웹 성능 지표(예: FCP, LCP, CLS 등)도 함께 고려해야 정확한 품질 진단이 가능합니다.앱은 빠르게 실행되더라도, 웹뷰 내부의 웹 콘텐츠 로딩이 느릴 경우 사용자는 전체적으로 느린 앱으로 인식하게 됩니다.웹뷰 포함 여부와 관계없이 모든 플랫폼에서 공통으로 고려할 수 있는 성능 지표입니다.서버나 백엔드와의 상호작용을 포함하는 모든 앱에서 기본적으로 측정됩니다.• None 응답 시간 (Response Time): 사용자의 요청에 대해 시스템이 응답하는 데 걸리는 시간• None API 지연 시간 (API Latency): 백엔드 서버와의 통신 시간• None CPU / 메모리 사용량: 실행 중인 프로세스가 소비하는 시스템 자원• None Page Load Time (PL): 화면 또는 페이지가 요청된 후 첫 번째 UI가 완전히 표시될 때까지의 시간 (3초를 넘으면 사용자의 43%가 이탈하며, 5초가 넘으면 이탈률이 90%까지 증가한다고 합니다.)• None 사용자 인터랙션 반응 시간: 사용자가 탭, 스크롤, 입력 등의 행동을 했을 때, 앱이 이에 반응하는 속도. UI 반응이 느리면 기능이 잘 작동해도 “느린 앱”으로 인식.• None Tap Latency: 탭 입력 후 UI 반영까지 걸리는 시간. 0.1초 (100ms) 가 '순간적(instantaneous)'으로 인식되며 가장 이상적. 0.5~1초는 '즉각적(immediate)'으로, 1초를 넘기면 체감 반응 속도가 급격히 떨어짐.• None Scroll Responsiveness: 스크롤 시작 후 화면이 부드럽고 빠르게 따라오는 정도. 스크롤 중 프레임 드롭이 발생하거나 16ms(60fps) 기준을 반복적으로 벗어나면 버벅임으로 인식됨.네이티브 앱 또는 웹뷰 앱 전체를 기준으로 측정할 때는 아래와 같은 앱 전용 성능 지표를 고려해야 합니다.사용자가 앱 내에서 탭을 이동하거나 버튼을 눌렀을 때 새 화면이 얼마나 빠르게 나타나는지를 측정합니다.특히, 탭 UI, 스크롤 가능한 콘텐츠, 네트워크 기반 페이지 등은 체감 성능에 큰 영향을 줍니다.• None Navigation Time: 현재 화면에서 다른 화면(예: 탭 이동, 버튼 클릭 등)으로 전환되는 데 걸리는 시간. 네트워크/애니메이션 영향 포함.• None Fragment Render Time: 안드로이드에서 프래그먼트(Fragment)처럼 구분된 UI 컴포넌트가 생성되어 화면에 완전히 렌더링될 때까지의 시간. 복합적인 UI일 경우 이 시간이 길어짐.• None Screen Rendering Time: 한 화면이 렌더링 완료되는 데 걸리는 시간. 16ms (60fps) 초과 시 프레임 드롭 체감.• None 프레임 드랍 (FPS, Jank Rate): 화면 렌더링 중 애니메이션이 끊기는 현상. 초당 프레임 수(FPS)와 프레임 드랍률. FPS가 낮거나 Jank가 많으면 사용자 체감 품질 저하.• None 메모리 사용량 (Memory Usage): 앱이 사용하는 RAM의 크기.웹뷰(WebView)나 하이브리드 앱에서 웹 콘텐츠가 로드되는 성능은 아래와 같은 웹 전용 지표를 통해 측정할 수 있습니다.이 지표들은 주로 Google Lighthouse나 Web Vitals 기반 도구로 측정할 수 있습니다.• None FCP (First Contentful Paint): 화면에 첫 콘텐츠가 표시되기까지 걸리는 시간• None LCP (Largest Contentful Paint): 주요 콘텐츠(예: 이미지, 텍스트 블록)가 로드되는 시간• None TTI (Time to Interactive): 페이지가 실제로 상호작용 가능한 상태가 되기까지 걸리는 시간• None CLS (Cumulative Layout Shift): 페이지 로딩 중 요소가 예상치 못하게 이동하는 정도• None 페이지 로딩 시간: 전체 HTML 문서가 로딩되기까지 걸리는 시간• None LCP(Largest Contentful Paint): 가장 큰 콘텐츠가 화면에 뜨는 데 걸린 시간• None CLS (Cumulative Layout Shift): 페이지가 로딩되면서 화면이 흔들리는 정도• None INP (Interaction to Next Paint): 사용자 클릭 등 상호작용에 반응하기까지의 시간Android Vitals의 주요 항목 중 APP Launch Time 성능을 알아 보겠습니다.APP Launch Time은 크게 Cold, Warm, Hot Start 세 가지 상태로 분류됩니다.• None Cold Start (콜드 스타트): 처음 앱을 실행했을 때 초기 로딩 속도를 말합니다. 앱을 완전히 처음부터 실행하기 때문에 가장 무거운 시작 방식입니다. 시스템 프로세스 생성 → Application.onCreate() 실행 → 액티비티 생성 및 초기 화면 렌더링 등 모든 과정이 포함합니다. 여기서 최적화하면 나머지 상태도 전반적으로 빠르게 개선될 수 있습니다. 그만큼 개선하기 어렵다는 뜻이기도 합니다.• None Warm Start (웜 스타트): 백그라운드에 있던 앱을 다시 열었을 때의 로딩 속도를 말합니다. 이미 앱 프로세스는 살아있으나, 새로운 액티비티 생성이 필요한 경우로 Cold Start
2025.07.31

좋아요

별로에요

바닥부터 시작하는 웹앱 에디터 개발기
안녕하세요, 커뮤니티실 모임팀의 프론트엔드 엔지니어 Louie(루이)예요. 오늘은 모임 서비스의 웹앱 환경에 에디터를 적용했던 경험을 공유하려고 해요.모바일 앱에서 게시글을 올려보신 적 있으신가요? 그때 텍스트를 꾸미고 이미지를 글 사이사이에 자유롭게 배치하셨다면 에디터 를 사용하신 거예요. 에디터는 볼드체, 취소선, 밑줄 같은 리치텍스트 기능은 물론, 글과 사진을 블록처럼 자유롭게 배치할 수 있는 글쓰기 도구예요. 흔히 모바일 앱의 에디터는 주로 iOS나 Android 같은 네이티브 환경에서 구현되어 있지만, 당근모임은 웹앱 기반의 서비스라 웹 기술로 에디터를 직접 개발해야 했어요.웹앱에서 에디터를 처음부터 직접 개발하는 건 기술적으로 쉽지 않은 도전이었어요. 네이티브 환경과는 달리 여러 가지 제약 사항을 고려해야 했고, 어디서부터 어떤 방식으로 구현해야 할지 막막했죠. 로컬 환경에서 테스트할 때는 잘 동작하더라도, 실제 앱 환경에서 예상치 못한 문제들이 자주 발생하기도 했어요. 그런 문제들을 어떻게 하나씩 풀어가며 웹앱 에디터를 개발할 수 있었는지 이야기 들려드릴게요!당근모임에는 왜 에디터가 필요했을까요?웹앱 에디터 개발 과정을 본격적으로 설명하기에 앞서, 먼저 모임팀이 에디터를 만들게 된 이유를 간단히 살펴볼게요.커뮤니티에는 다양한 사람들이 다양한 방식으로 콘텐츠를 전달하고 싶어 해요. 하지만 기존 당근모임에서는 게시글을 작성할 때 사진과 동영상을 자유롭게 배치할 수 없었어요. 첨부한 이미지와 영상은 모두 1:1 비율의 썸네일로, 텍스트와 분리된 채 노출됐죠. 게시글을 클릭해도 글이 먼저 제시되고, 사진과 영상은 마지막에 일괄적으로 붙는 구조였고요.이런 상황에서 글쓰기 사용 경험 조사를 진행한 결과, 사용자들은 이미지의 자유로운 배치, 리치텍스트 기능 등을 원하고 있었어요. 사용자들이 정보성 있는 글을 더 자유롭게 작성할 수 있도록 지원하려면, 기존의 글쓰기 방식을 벗어나는 변화가 필요했던 거죠.Step 1. 에디터의 구조 이해하기에디터는 블록 단위로 콘텐츠를 관리하기 때문에 글과 사진을 제한 없이 자유롭게 배치할 수 있어요. 각 블록은 기본적으로 type이라는 속성과 그에 필요한 추가적인 데이터를 가지고 있어요. 예를 들어 문단 블록의 경우는 글자 스타일(볼드, 이탤릭 등)에 따라 내부에서 여러 개의 text 노드들을 그룹화해요. 이미지 블록은 이미지의 URL이나 설명과 같은 정보를 별도의 데이터로 관리하죠.이러한 블록 구조를 활용하면 사진 블록과 텍스트 블록을 독립적인 개체로 취급할 수 있어요. 덕분에 서로의 위치를 원하는 대로 쉽게 조정할 수 있죠. 사용자는 블록을 드래그하여 위치를 바꾸거나, 새로운 블록을 원하는 위치에 삽입하는 등 직관적으로 조작할 수도 있고요.에디터 구현을 계획하면서 가장 중요하게 고민했던 문제는 블록 데이터를 서버에 어떻게 저장할지였어요. 서버 엔지니어와 논의하며 크게 두 가지 방법을 고민했어요.첫 번째 방법은 블록 스키마 전체를 단순히 JSON 형태로 데이터베이스에 저장하고 불러오는 방식이었어요. 이 방법은 구현이 쉽고 빠르지만, 확장성 측면에서 큰 단점이 있었어요. JSON으로 데이터를 저장하면 각 블록 항목에 대한 인덱싱이 불가능하고, 데이터가 손상되었을 때 수정이 어려우며, 마이그레이션 역시 거의 불가능하다는 단점이 존재했어요. 특히 기존 모임 서비스에 이미 발행된 게시글과 호환성을 유지해야 했기 때문에, JSON으로 저장하는 방식은 적합하지 않다고 판단했어요.결국 선택한 두 번째 방법은 에디터에서 필수적으로 저장해야 하는 값들만 별도로 추출하여 서버에 저장하는 것이었어요. 예를 들어 이미지의 경우 presigned URL과 같은 필수 데이터를, 텍스트의 경우 실제 콘텐츠 내용을 DB에 저장하도록 했어요. 서버에서 데이터를 불러올 때는 이렇게 저장된 핵심 데이터만 가져와, 에디터에서 필요한 형태로 재구성하는 방식으로 설계했어요.핵심 아이디어는 서버가 에디터의 모든 블록 구조를 완벽히 이해할 필요가 없다는 점이에요. 서버는 필수적인 데이터만 관리하고, 에디터가 이를 기반으로 블록 구조를 재구성하여 상호 통신하는 방식으로 구현했어요. 이를 통해 서버의 확장성과 데이터 관리의 유연성을 확보할 수 있었어요.Step 2. 기술 구현 방법 정하기가장 먼저 고민했던 부분은 에디터를 구현할 기술 스택이었어요. Quill, Tiptap, ProseMirror 등 여러 옵션 중에서 특히 ProseMirror와 Tiptap을 집중적으로 검토했어요. ProseMirror와 Tiptap의 장단점을 대략적으로 비교하면 아래와 같아요.ProseoMirror장점: 에디터를 구현하기 위한 가장 기본적이면서도 필수적인 기능을 제공하고, headless한 구조로 뛰어난 확장성을 지녔어요.단점: 스키마에 대한 설계, transaction, view 등 개념 계층에 대한 이해가 필요했고, 기초적으로 제공되는 툴을 직접 사용하여 원하는 기능을 하나씩 구현해야 하기 때문에 초기 진입 장벽이 높아요.Tiptap장점: ProseMirror를 보다 쉽게 사용할 수 있도록 추가적으로 래핑한 형태로, 빠른 개발 속도와 초기 프로토타이핑이 가능해요.단점: 디버깅의 복잡도, 여러 패키지에 대한 의존성, 확장성의 문제들이 존재해요.초기 프로토타입을 신속하게 제작하기 위해선 Tiptap을 사용하는 게 합리적이겠지만, 장기적 관점에서 Tiptap이 더 많은 비용을 발생시킬 것이라 판단했어요. Tiptap이 가진 단점을 좀 더 구체적으로 살펴볼게요.디버깅의 복잡성: Tiptap을 사용하면 디버깅 스택이 Tiptap ProseMirror DOM Mutation과 같은 형태로 깊어질 수 있었고, 중간에 익명 함수 등이 포함되어 오류의 정확한 위치를 찾기 어려워질 가능성이 있었어요. 실제로 관련 사례들이 있기도 했어요. (Tiptap 이슈, ProseMirror 토론)패키지 의존성과 번들 크기 문제: Tiptap과 다양한 Extension을 함께 사용하면 npm 패키지 의존성이 급격히 증가하고 번들 크기도 커져서 관리 비용이 높아질 수 있었어요. 래퍼의 벽 이 발생할 가능성: ProseMirr
nodejs
7/31/2025

바닥부터 시작하는 웹앱 에디터 개발기
안녕하세요, 커뮤니티실 모임팀의 프론트엔드 엔지니어 Louie(루이)예요. 오늘은 모임 서비스의 웹앱 환경에 에디터를 적용했던 경험을 공유하려고 해요.모바일 앱에서 게시글을 올려보신 적 있으신가요? 그때 텍스트를 꾸미고 이미지를 글 사이사이에 자유롭게 배치하셨다면 에디터 를 사용하신 거예요. 에디터는 볼드체, 취소선, 밑줄 같은 리치텍스트 기능은 물론, 글과 사진을 블록처럼 자유롭게 배치할 수 있는 글쓰기 도구예요. 흔히 모바일 앱의 에디터는 주로 iOS나 Android 같은 네이티브 환경에서 구현되어 있지만, 당근모임은 웹앱 기반의 서비스라 웹 기술로 에디터를 직접 개발해야 했어요.웹앱에서 에디터를 처음부터 직접 개발하는 건 기술적으로 쉽지 않은 도전이었어요. 네이티브 환경과는 달리 여러 가지 제약 사항을 고려해야 했고, 어디서부터 어떤 방식으로 구현해야 할지 막막했죠. 로컬 환경에서 테스트할 때는 잘 동작하더라도, 실제 앱 환경에서 예상치 못한 문제들이 자주 발생하기도 했어요. 그런 문제들을 어떻게 하나씩 풀어가며 웹앱 에디터를 개발할 수 있었는지 이야기 들려드릴게요!당근모임에는 왜 에디터가 필요했을까요?웹앱 에디터 개발 과정을 본격적으로 설명하기에 앞서, 먼저 모임팀이 에디터를 만들게 된 이유를 간단히 살펴볼게요.커뮤니티에는 다양한 사람들이 다양한 방식으로 콘텐츠를 전달하고 싶어 해요. 하지만 기존 당근모임에서는 게시글을 작성할 때 사진과 동영상을 자유롭게 배치할 수 없었어요. 첨부한 이미지와 영상은 모두 1:1 비율의 썸네일로, 텍스트와 분리된 채 노출됐죠. 게시글을 클릭해도 글이 먼저 제시되고, 사진과 영상은 마지막에 일괄적으로 붙는 구조였고요.이런 상황에서 글쓰기 사용 경험 조사를 진행한 결과, 사용자들은 이미지의 자유로운 배치, 리치텍스트 기능 등을 원하고 있었어요. 사용자들이 정보성 있는 글을 더 자유롭게 작성할 수 있도록 지원하려면, 기존의 글쓰기 방식을 벗어나는 변화가 필요했던 거죠.Step 1. 에디터의 구조 이해하기에디터는 블록 단위로 콘텐츠를 관리하기 때문에 글과 사진을 제한 없이 자유롭게 배치할 수 있어요. 각 블록은 기본적으로 type이라는 속성과 그에 필요한 추가적인 데이터를 가지고 있어요. 예를 들어 문단 블록의 경우는 글자 스타일(볼드, 이탤릭 등)에 따라 내부에서 여러 개의 text 노드들을 그룹화해요. 이미지 블록은 이미지의 URL이나 설명과 같은 정보를 별도의 데이터로 관리하죠.이러한 블록 구조를 활용하면 사진 블록과 텍스트 블록을 독립적인 개체로 취급할 수 있어요. 덕분에 서로의 위치를 원하는 대로 쉽게 조정할 수 있죠. 사용자는 블록을 드래그하여 위치를 바꾸거나, 새로운 블록을 원하는 위치에 삽입하는 등 직관적으로 조작할 수도 있고요.에디터 구현을 계획하면서 가장 중요하게 고민했던 문제는 블록 데이터를 서버에 어떻게 저장할지였어요. 서버 엔지니어와 논의하며 크게 두 가지 방법을 고민했어요.첫 번째 방법은 블록 스키마 전체를 단순히 JSON 형태로 데이터베이스에 저장하고 불러오는 방식이었어요. 이 방법은 구현이 쉽고 빠르지만, 확장성 측면에서 큰 단점이 있었어요. JSON으로 데이터를 저장하면 각 블록 항목에 대한 인덱싱이 불가능하고, 데이터가 손상되었을 때 수정이 어려우며, 마이그레이션 역시 거의 불가능하다는 단점이 존재했어요. 특히 기존 모임 서비스에 이미 발행된 게시글과 호환성을 유지해야 했기 때문에, JSON으로 저장하는 방식은 적합하지 않다고 판단했어요.결국 선택한 두 번째 방법은 에디터에서 필수적으로 저장해야 하는 값들만 별도로 추출하여 서버에 저장하는 것이었어요. 예를 들어 이미지의 경우 presigned URL과 같은 필수 데이터를, 텍스트의 경우 실제 콘텐츠 내용을 DB에 저장하도록 했어요. 서버에서 데이터를 불러올 때는 이렇게 저장된 핵심 데이터만 가져와, 에디터에서 필요한 형태로 재구성하는 방식으로 설계했어요.핵심 아이디어는 서버가 에디터의 모든 블록 구조를 완벽히 이해할 필요가 없다는 점이에요. 서버는 필수적인 데이터만 관리하고, 에디터가 이를 기반으로 블록 구조를 재구성하여 상호 통신하는 방식으로 구현했어요. 이를 통해 서버의 확장성과 데이터 관리의 유연성을 확보할 수 있었어요.Step 2. 기술 구현 방법 정하기가장 먼저 고민했던 부분은 에디터를 구현할 기술 스택이었어요. Quill, Tiptap, ProseMirror 등 여러 옵션 중에서 특히 ProseMirror와 Tiptap을 집중적으로 검토했어요. ProseMirror와 Tiptap의 장단점을 대략적으로 비교하면 아래와 같아요.ProseoMirror장점: 에디터를 구현하기 위한 가장 기본적이면서도 필수적인 기능을 제공하고, headless한 구조로 뛰어난 확장성을 지녔어요.단점: 스키마에 대한 설계, transaction, view 등 개념 계층에 대한 이해가 필요했고, 기초적으로 제공되는 툴을 직접 사용하여 원하는 기능을 하나씩 구현해야 하기 때문에 초기 진입 장벽이 높아요.Tiptap장점: ProseMirror를 보다 쉽게 사용할 수 있도록 추가적으로 래핑한 형태로, 빠른 개발 속도와 초기 프로토타이핑이 가능해요.단점: 디버깅의 복잡도, 여러 패키지에 대한 의존성, 확장성의 문제들이 존재해요.초기 프로토타입을 신속하게 제작하기 위해선 Tiptap을 사용하는 게 합리적이겠지만, 장기적 관점에서 Tiptap이 더 많은 비용을 발생시킬 것이라 판단했어요. Tiptap이 가진 단점을 좀 더 구체적으로 살펴볼게요.디버깅의 복잡성: Tiptap을 사용하면 디버깅 스택이 Tiptap ProseMirror DOM Mutation과 같은 형태로 깊어질 수 있었고, 중간에 익명 함수 등이 포함되어 오류의 정확한 위치를 찾기 어려워질 가능성이 있었어요. 실제로 관련 사례들이 있기도 했어요. (Tiptap 이슈, ProseMirror 토론)패키지 의존성과 번들 크기 문제: Tiptap과 다양한 Extension을 함께 사용하면 npm 패키지 의존성이 급격히 증가하고 번들 크기도 커져서 관리 비용이 높아질 수 있었어요. 래퍼의 벽 이 발생할 가능성: ProseMirr
2025.07.31
nodejs

좋아요

별로에요

(인터뷰) AI와 함께 10시간 만에 서비스 개발하기
2025 카카오 사내 해커톤 ‘10K’ 우승팀이 말하는 '바이브 코딩’의 모든 것최근 카카오에서 열린 사내 해커톤 '10K’는 AI가 개발 문화와 생산성의 패러다임을 어떻게 바꾸고 있는지를 보여주는 상징적인 행사였습니다. 6개월 이상 걸릴 수 있었던 프로젝트를 단 10시간 만에 완성하며 우승을 차지한 ‘이게 모에요’ 팀.그들이 활용한 핵심 비결, 에 대한 생생한 경험과 미래에 대한 인사이트를 공유합니다.새로운 놀이터: 해커톤과24시간에서 10시간으로, AI가 만든 변화카카오는 2013년부터 매년 '크루를 위한 24시간’이라는 의미의 해커톤을 진행해왔습니다. 하지만 올해, 그 이름은 로 바뀌었습니다. 개발 시간이 10시간으로 대폭 단축된 것입니다.이러한 혁신적인 변화는 AI 기반 개발 기법의 전면적인 도입이 있었기에 가능했습니다. 참가자들은 기획부터 최소기능제품(MVP) 구현, 발표까지 전 과정에서 AI의 도움을 받을 수 있었습니다.이번 해커톤의 또 다른 특징은 1차 심사를 구글과 OpenAI의 AI 모델이 담당했다는 점입니다. AI는 참가팀이 작성한 코드의 완성도, 효율성, 사용자 경험(UX) 등을 종합적으로 평가하고 개선을 위한 피드백까지 제공했습니다.해커톤에서 1위를 차지한 ‘이게 모에요’ 팀(신호석, 김수정, 홍민주 님)은 AI 챗봇을 접목한 크롬 확장 프로그램을 선보였습니다.• 핵심 기능: 커머스 서비스 판매자들이 자연어 대화로 재고 관리, 판매 실적 확인 가능• 특장점: 판매 지표 중 이상 수치(Anomaly)를 감지하면 화면에 시각적으로 강조하여 직관적인 인사이트 제공사실 우승팀은 모두 백엔드 서버 개발자였습니다. 평소에 주로 다루는 가 아닌 과 를 사용해 프론트엔드 영역인 크롬 확장 프로그램을 개발하는 것은 큰 도전이었습니다.팀은 와 AI 기반 통합개발환경(IDE) 를 적극적으로 활용했습니다. 이를 통해 개발 속도를 획기적으로 높였을 뿐만 아니라, 기능 구현과 방향성 설정에 대한 의사결정을 빠르고 유연하게 진행하며 프로젝트의 완성도를 높일 수 있었습니다.미래를 향하여: AI, 개발, 그리고 우리AI는 생산성을 극대화하는 ‘훌륭한 동료’의 놀라운 성능은 'AI가 개발자를 대체할 것인가?'라는 질문을 던지게 합니다. 하지만 세 명의 개발자 모두 ‘AI가 아직까지는 개발자를 대체하기는 어려울 것’이라고 입을 모읍니다.AI는 뛰어난 코드 생성 능력을 갖췄지만, 여러 시스템의 복합적인 맥락을 깊이 이해하고, 비즈니스적 판단에 따른 '선택’과 그에 대한 '책임’을 지는 능력은 부족하기 때문입니다. AI는 개발자의 역할을 빼앗는 것이 아니라, 생산성을 극대화하는 훌륭한 보조 도구이자 동료가 될 것입니다.모두를 위한 개발의 가능성그렇다면 비개발자도 프로그램을 만들 수 있을까요? 팀원들은 '어느 정도 가능하다’고 답합니다. 프로그래밍의 기본적인 구조에 대한 지식을 갖추고, 원하는 기능을 AI에게 명확히 설명할 수만 있다면 을 통해 누구나 자신의 아이디어를 현실로 만들 수 있는 시대가 오고 있습니다.AI는 개발의 패러다임을 바꾸고 있습니다. 아이디어를 실제 서비스로 구현하는 과정의 장벽을 허물고, 우리 모두에게 더 많은 창작의 기회를 열어주고 있습니다.
7/31/2025

(인터뷰) AI와 함께 10시간 만에 서비스 개발하기
2025 카카오 사내 해커톤 ‘10K’ 우승팀이 말하는 '바이브 코딩’의 모든 것최근 카카오에서 열린 사내 해커톤 '10K’는 AI가 개발 문화와 생산성의 패러다임을 어떻게 바꾸고 있는지를 보여주는 상징적인 행사였습니다. 6개월 이상 걸릴 수 있었던 프로젝트를 단 10시간 만에 완성하며 우승을 차지한 ‘이게 모에요’ 팀.그들이 활용한 핵심 비결, 에 대한 생생한 경험과 미래에 대한 인사이트를 공유합니다.새로운 놀이터: 해커톤과24시간에서 10시간으로, AI가 만든 변화카카오는 2013년부터 매년 '크루를 위한 24시간’이라는 의미의 해커톤을 진행해왔습니다. 하지만 올해, 그 이름은 로 바뀌었습니다. 개발 시간이 10시간으로 대폭 단축된 것입니다.이러한 혁신적인 변화는 AI 기반 개발 기법의 전면적인 도입이 있었기에 가능했습니다. 참가자들은 기획부터 최소기능제품(MVP) 구현, 발표까지 전 과정에서 AI의 도움을 받을 수 있었습니다.이번 해커톤의 또 다른 특징은 1차 심사를 구글과 OpenAI의 AI 모델이 담당했다는 점입니다. AI는 참가팀이 작성한 코드의 완성도, 효율성, 사용자 경험(UX) 등을 종합적으로 평가하고 개선을 위한 피드백까지 제공했습니다.해커톤에서 1위를 차지한 ‘이게 모에요’ 팀(신호석, 김수정, 홍민주 님)은 AI 챗봇을 접목한 크롬 확장 프로그램을 선보였습니다.• 핵심 기능: 커머스 서비스 판매자들이 자연어 대화로 재고 관리, 판매 실적 확인 가능• 특장점: 판매 지표 중 이상 수치(Anomaly)를 감지하면 화면에 시각적으로 강조하여 직관적인 인사이트 제공사실 우승팀은 모두 백엔드 서버 개발자였습니다. 평소에 주로 다루는 가 아닌 과 를 사용해 프론트엔드 영역인 크롬 확장 프로그램을 개발하는 것은 큰 도전이었습니다.팀은 와 AI 기반 통합개발환경(IDE) 를 적극적으로 활용했습니다. 이를 통해 개발 속도를 획기적으로 높였을 뿐만 아니라, 기능 구현과 방향성 설정에 대한 의사결정을 빠르고 유연하게 진행하며 프로젝트의 완성도를 높일 수 있었습니다.미래를 향하여: AI, 개발, 그리고 우리AI는 생산성을 극대화하는 ‘훌륭한 동료’의 놀라운 성능은 'AI가 개발자를 대체할 것인가?'라는 질문을 던지게 합니다. 하지만 세 명의 개발자 모두 ‘AI가 아직까지는 개발자를 대체하기는 어려울 것’이라고 입을 모읍니다.AI는 뛰어난 코드 생성 능력을 갖췄지만, 여러 시스템의 복합적인 맥락을 깊이 이해하고, 비즈니스적 판단에 따른 '선택’과 그에 대한 '책임’을 지는 능력은 부족하기 때문입니다. AI는 개발자의 역할을 빼앗는 것이 아니라, 생산성을 극대화하는 훌륭한 보조 도구이자 동료가 될 것입니다.모두를 위한 개발의 가능성그렇다면 비개발자도 프로그램을 만들 수 있을까요? 팀원들은 '어느 정도 가능하다’고 답합니다. 프로그래밍의 기본적인 구조에 대한 지식을 갖추고, 원하는 기능을 AI에게 명확히 설명할 수만 있다면 을 통해 누구나 자신의 아이디어를 현실로 만들 수 있는 시대가 오고 있습니다.AI는 개발의 패러다임을 바꾸고 있습니다. 아이디어를 실제 서비스로 구현하는 과정의 장벽을 허물고, 우리 모두에게 더 많은 창작의 기회를 열어주고 있습니다.
2025.07.31

좋아요

별로에요

Node.js 와 OpenAI Realtime Websocket API 로 만드는 Voice Chat 서비스
이 글은 Devocean 에 올라온 Go로 만드는 실시간 음성 챗봇: OpenAI Realtime API를 가장 쉽게 쓰는 법 (Go routine + Go channel) 글을 읽다가 영감을 받아Node.js의 Stream으로 만들어도 간단한 예제를 작성할 수 있을 것 같아서 글을 적어봅니다.Node.js의 Stream 도 Go언어의 Channel 처럼 간단히 연결할 수 있으며, Node.js의 내장 Eventloop는 이미 강력한 비동기 처리를 지원하기 때문입니다.OpenAI Realtime Websocket API를 사용하여 만드는 Voice Chat 서비스이 글에서는 API 형식의 연결이 아니라 맥북에 있는 마이크를 통해 음성을 입력받아서 OpenAI Realtime Websocket API에 Stream으로 보내고,API응답으로 오는 Websocket 데이터를 맥북의 스피커로 출력하는 예제를 작성해보도록 하겠습니다.윈도우나 리눅스 환경에서는 테스트해보지 않았음을 미리 알려드립니다.또한 맥북에 내장된 마이크와 스피커를 사용하는 경우 스피커로 나오는 출력이 마이크로 다시 입력되어 무한히 반복되는 loopback 문제가 있으므로 헤드폰 사용을 권장합니다.또한 마이크와 스피커 사용을 위해서는 마이크와 스피커 권한이 필요합니다.• None 마이크가 내장된 이어폰 or 헤드폰 (권장)먼저 node.js 환경을 설정해야 합니다. node.js 설치는 node.js 공식 홈페이지 에서 다운로드 받아 설치하면 됩니다.여러 버전의 node.js를 설치하고 싶다면 nvm 을 사용하면 됩니다.마이크가 내장된 헤드폰이 있다면 블루투스로 PC에 연결해주세요. 이걸로 준비는 끝났습니다.Node.js로 만드는 실시간 Voice Chat 서비스OpenAI Realtime API 는 Websocket 프로토콜을 지원하므로,여기서 우리는 mic의 출력을 OpenAI Realtime API의 입력 stream으로 연결하고, 응답으로 오는 websocket stream을 스피커로 그대로 연결해보겠습니다.먼저 필요한 라이브러리를 설치합니다.그리고 필요한 라이브러리를 불러옵시다.먼저 OpenAI Realtime API Websocket 에 연결하고 필요한 Handler를 등록합니다.이제 마이크의 출력을 OpenAI Realtime API의 Websocket 입력 stream으로 연결해봅시다.이제 벌써 마지막입니다. 이제 마이크의 출력을 OpenAI Realtime API의 입력 stream으로 연결했으니, 이제 응답으로 오는 websocket stream을 스피커로 출력해봅시다.자 이제 모든 준비가 끝났습니다. 위에 적은 코드를 합쳐서 동작 가능한 코드를 완성해봅시다.Go channel 대신 Node.js stream을 이용해서 Voice Chat을 만들어 봤습니다.이렇게 간단하게 만들 수 있는 이유는 Node.js의 내장 Eventloop가 이미 강력한 비동기 처리를 지원하기 때문입니다.Text 위주의 LLM 사용예가 많지만 Voice, Image, Video 등의 멀티미디어 처리가 앞으로 다양하게 사용될 것이라고 생각하여 이번 예제를 작성해봤습니다.• None Go로 만드는 실시간 음성 챗봇: OpenAI Realtime API를 가장 쉽게 쓰는 법 (Go routine + Go channel)
go
nodejs
7/30/2025

Node.js 와 OpenAI Realtime Websocket API 로 만드는 Voice Chat 서비스
이 글은 Devocean 에 올라온 Go로 만드는 실시간 음성 챗봇: OpenAI Realtime API를 가장 쉽게 쓰는 법 (Go routine + Go channel) 글을 읽다가 영감을 받아Node.js의 Stream으로 만들어도 간단한 예제를 작성할 수 있을 것 같아서 글을 적어봅니다.Node.js의 Stream 도 Go언어의 Channel 처럼 간단히 연결할 수 있으며, Node.js의 내장 Eventloop는 이미 강력한 비동기 처리를 지원하기 때문입니다.OpenAI Realtime Websocket API를 사용하여 만드는 Voice Chat 서비스이 글에서는 API 형식의 연결이 아니라 맥북에 있는 마이크를 통해 음성을 입력받아서 OpenAI Realtime Websocket API에 Stream으로 보내고,API응답으로 오는 Websocket 데이터를 맥북의 스피커로 출력하는 예제를 작성해보도록 하겠습니다.윈도우나 리눅스 환경에서는 테스트해보지 않았음을 미리 알려드립니다.또한 맥북에 내장된 마이크와 스피커를 사용하는 경우 스피커로 나오는 출력이 마이크로 다시 입력되어 무한히 반복되는 loopback 문제가 있으므로 헤드폰 사용을 권장합니다.또한 마이크와 스피커 사용을 위해서는 마이크와 스피커 권한이 필요합니다.• None 마이크가 내장된 이어폰 or 헤드폰 (권장)먼저 node.js 환경을 설정해야 합니다. node.js 설치는 node.js 공식 홈페이지 에서 다운로드 받아 설치하면 됩니다.여러 버전의 node.js를 설치하고 싶다면 nvm 을 사용하면 됩니다.마이크가 내장된 헤드폰이 있다면 블루투스로 PC에 연결해주세요. 이걸로 준비는 끝났습니다.Node.js로 만드는 실시간 Voice Chat 서비스OpenAI Realtime API 는 Websocket 프로토콜을 지원하므로,여기서 우리는 mic의 출력을 OpenAI Realtime API의 입력 stream으로 연결하고, 응답으로 오는 websocket stream을 스피커로 그대로 연결해보겠습니다.먼저 필요한 라이브러리를 설치합니다.그리고 필요한 라이브러리를 불러옵시다.먼저 OpenAI Realtime API Websocket 에 연결하고 필요한 Handler를 등록합니다.이제 마이크의 출력을 OpenAI Realtime API의 Websocket 입력 stream으로 연결해봅시다.이제 벌써 마지막입니다. 이제 마이크의 출력을 OpenAI Realtime API의 입력 stream으로 연결했으니, 이제 응답으로 오는 websocket stream을 스피커로 출력해봅시다.자 이제 모든 준비가 끝났습니다. 위에 적은 코드를 합쳐서 동작 가능한 코드를 완성해봅시다.Go channel 대신 Node.js stream을 이용해서 Voice Chat을 만들어 봤습니다.이렇게 간단하게 만들 수 있는 이유는 Node.js의 내장 Eventloop가 이미 강력한 비동기 처리를 지원하기 때문입니다.Text 위주의 LLM 사용예가 많지만 Voice, Image, Video 등의 멀티미디어 처리가 앞으로 다양하게 사용될 것이라고 생각하여 이번 예제를 작성해봤습니다.• None Go로 만드는 실시간 음성 챗봇: OpenAI Realtime API를 가장 쉽게 쓰는 법 (Go routine + Go channel)
2025.07.30
go
nodejs

좋아요

별로에요

EMNLP24 늦은 후기 1탄: 텔레콤 LLM 벤치마크 TelBench 발표 이야기
고객센터는 왜 ‘텔코 LLM’을 꿈꾸었는가?통신사 고객센터의 하루는 복잡합니다. 각기 다른 상담원이 여러 차례 전화를 받으며 문제를 파악하고, 요금제와 부가서비스를 안내하고, 후속 조치를 남겨야 합니다.대화는 길고 전문 용어가 많으며, 고객의 감정도 제각각입니다. 이런 과정을 일관성 있게 처리하려면 숙련된 상담사가 필요하지만, 교육과 유지에는 많은 시간과 비용이 듭니다.바로 이 지점에서 SK텔레콤 AI Data Engineering팀은 ‘텔코 특화 LLM’의 가능성을 보았습니다.기존 범용 LLM이 상담 대화 요약과 후속 업무를 자동화할 수 있다면 상담 효율이 크게 향상될 수 있을 것입니다. 그러나 현실은 녹록지 않았습니다.일반 LLM은 통신 도메인의 복잡한 제품명이나 요금제 이름을 잘 알아듣지 못하고, 한국어 데이터도 부족한 듯 보였습니다.그래서 저희 팀은 고객센터 업무에 맞춘 자체 벤치마크와 학습 데이터셋을 만들었습니다.TelBench: 한국어 상담 데이터를 기반으로 한 첫 통신 벤치마크통신 업무를 세분화한 10가지 과제저희가 개발한 TelBench는 고객센터 상담의 ‘사후처리(Post‑work)’ 단계를 개선하기 위해 설계된 벤치마크입니다.상담 로그에서 고객 감정을 분류하는 Sentiment, 요금제나 기기 이름을 찾아내는 Entity 인식, 고객 의도를네 가지 유형(문의/확인/취소/신규)으로 구분하는 Intent 등 TelTask 7개 및 TelInstruct 3개 Task로 구성되어 있습니다.특히 Summary와 To‑Do 과제는 상담 종료 후 상담사가 해야 할 일을 자동으로 정리해 주는 데 초점을 둡니다.요약 과제는 대화 속 핵심 정보와 통신 전문 용어를 정확히 담아야 하며, To‑Do 과제는 MMS 발송, 계정 소유자 동의 확인, 추가 조사 요청 등 후속 조치를 구체적으로 제안합니다.또한 Safety 과제는 한국어 상담 상황에서 나올 수 있는 민감한 표현을 탐지하도록 설계되었습니다.데이터는 모두 한국어로 이루어져 있으며, 사람(링귀스트 및 어노테이터)이 직접 검수한 수백개(각 Task 당)의 benchmark set으로 구성해 실제 상담 환경을 충실히 반영했습니다.단, TelBench 데이터는 고객 개인정보를 보호하기 위해 모든 상담 기록에서 이름, 전화번호 같은 식별 정보를 제거한 후 사용합니다.모델이 학습 과정에서 민감한 내용을 흡수하지 않도록 대화 속 개인정보 표현을 다른 임의의 값으로 치환합니다.모델이 출력할 때도 프롬프트 설계로 개인정보를 재구성하지 못하게 하는 등 프라이버시 보호에 신경을 썼습니다.LLM 평가 결과(‘24)와 오픈소스 모델의 가능성작년 저희 팀은 TelBench를 통해 여러 상용 및 오픈소스 LLM을 평가했습니다.Claude 3.5 Sonnet이 전반적으로 가장 뛰어난 성능을 보였지만,Llama‑3.1‑405B‑Instruct‑FP8나 Mistral‑Large‑Instruct 같은 ‘24년 기준 최신 오픈소스 모델도 요약이나 감정 분류에서는 상용 모델과 비슷한 성능을 냈습니다.하지만 통신 지식이 많이 필요한 엔티티 인식과 의도 분류에서는 오픈소스 모델이 상용 모델보다 약 0.1~0.2 정도 낮은 성능을 보였습니다.이러한 결과는 “오픈소스 모델로 파인튜닝을 해봤지만 결과가 좋지 않았다”는 유럽 연구자의 경험과도 동일했습니다.도메인 지식이 필요한 과제에서는 아직 상용 모델이 앞서지만, 오픈소스 모델의 개선 속도가 빠르므로 장기적으로는 대안을 제공할 수 있을 것으로 전망했습니다.포스터 세션에서 주요 질문과 답변포스터 발표에서는 국내외 참가자들이 다양한 질문을 던졌습니다. 주요 내용은 다음과 같습니다.• None 영어 지원 계획: 현재 TelBench는 한국어 데이터만 포함하지만, 향후 영어를 포함한 다국어 지원을 계획하고 있습니다.• None 데이터 공개 여부: 사내 정책상 전체 데이터셋을 공개하지는 않으며, 논문 부록에 일부 샘플만 수록했습니다.• None 모델 사용 현황: Telco‑LLM은 현재 SKT 고객센터의 상담사 업무를 지원하며, 상담 내역 요약과 후속 조치(To‑Do) 생성에 활용됩니다.• None 연구 vs. 엔지니어링: 팀의 업무는 연구와 엔지니어링가 반반입니다. 데이터 구축, 모델 개발뿐 아니라 실제 서비스 배포까지 담당합니다.• None 다국어 모델 개발: 다국어 지원은 아직 초기 단계이며, 내년부터 본격적으로 추진할 계획이라고 밝혔습니다.• None 프라이버시 보호: 음성인식 이후 데이터를 가명화한 데이터를 사용하며, 프롬프트로 민감한 정보 추출을 방지합니다.학회 중에 ‘Language Agents: Foundations, Prospects and Risks’라는 튜토리얼 세션이 가장 인상 깊었습니다.이 세션은 LLM 기반 에이전트가 단순한 언어 모델의 래퍼가 아니라 “언어를 통해 추론과 의사소통을 수행하는 새로운 세대의 에이전트”라고 정의하며,Reasoning, Memory, Planning과 같은 기본 구성 요소부터 멀티 에이전트 시스템, 안전성 문제까지 폭넓게 다뤘습니다. (튜토리얼의 세부 내용은 따로 글을 써서 공유할 예정입니다.)개인적인 성찰과 앞으로의 계획EMNLP 2024 발표를 준비하면서 느낀 가장 큰 보람은 산업 현장의 문제를 해결하는 연구가 얼마나 큰 가치를 지니는지 확인한 것입니다.SK텔레콤 AI Data Engineering팀으로서, 상담사들의 업무 부담을 줄이고 서비스 품질을 향상시키는 도구를 만들기 위해 데이터 수집, 익명화, 모델 평가 등 다양한 도전을 해왔습니다.TelBench를 통해 우리는 텔코 도메인에서 필요한 과제를 명확히 정의하고, 객관적인 비교 기준을 제시할 수 있었습니다.학회 기간에 만난 여러 국내외 연구자와 엔지니어들이 “이런 데이터셋이 있었으면 좋겠다”는 공감을 보여준 것도 큰 힘이 되었습니다.한편 2025년 2월 GSMA가 ‘Open‑Telco LLM Benchmarks’라는 오픈 커뮤니티를 출범시키며,통신 도메인에서 LLM의 한계를 지적하고 투명한 벤치마크의 필요성을 강조했습니다(gsma-benchmarks).GPT‑4가 TeleQnA 데이터셋에서 75% 미만의 점수를
7/29/2025

EMNLP24 늦은 후기 1탄: 텔레콤 LLM 벤치마크 TelBench 발표 이야기
고객센터는 왜 ‘텔코 LLM’을 꿈꾸었는가?통신사 고객센터의 하루는 복잡합니다. 각기 다른 상담원이 여러 차례 전화를 받으며 문제를 파악하고, 요금제와 부가서비스를 안내하고, 후속 조치를 남겨야 합니다.대화는 길고 전문 용어가 많으며, 고객의 감정도 제각각입니다. 이런 과정을 일관성 있게 처리하려면 숙련된 상담사가 필요하지만, 교육과 유지에는 많은 시간과 비용이 듭니다.바로 이 지점에서 SK텔레콤 AI Data Engineering팀은 ‘텔코 특화 LLM’의 가능성을 보았습니다.기존 범용 LLM이 상담 대화 요약과 후속 업무를 자동화할 수 있다면 상담 효율이 크게 향상될 수 있을 것입니다. 그러나 현실은 녹록지 않았습니다.일반 LLM은 통신 도메인의 복잡한 제품명이나 요금제 이름을 잘 알아듣지 못하고, 한국어 데이터도 부족한 듯 보였습니다.그래서 저희 팀은 고객센터 업무에 맞춘 자체 벤치마크와 학습 데이터셋을 만들었습니다.TelBench: 한국어 상담 데이터를 기반으로 한 첫 통신 벤치마크통신 업무를 세분화한 10가지 과제저희가 개발한 TelBench는 고객센터 상담의 ‘사후처리(Post‑work)’ 단계를 개선하기 위해 설계된 벤치마크입니다.상담 로그에서 고객 감정을 분류하는 Sentiment, 요금제나 기기 이름을 찾아내는 Entity 인식, 고객 의도를네 가지 유형(문의/확인/취소/신규)으로 구분하는 Intent 등 TelTask 7개 및 TelInstruct 3개 Task로 구성되어 있습니다.특히 Summary와 To‑Do 과제는 상담 종료 후 상담사가 해야 할 일을 자동으로 정리해 주는 데 초점을 둡니다.요약 과제는 대화 속 핵심 정보와 통신 전문 용어를 정확히 담아야 하며, To‑Do 과제는 MMS 발송, 계정 소유자 동의 확인, 추가 조사 요청 등 후속 조치를 구체적으로 제안합니다.또한 Safety 과제는 한국어 상담 상황에서 나올 수 있는 민감한 표현을 탐지하도록 설계되었습니다.데이터는 모두 한국어로 이루어져 있으며, 사람(링귀스트 및 어노테이터)이 직접 검수한 수백개(각 Task 당)의 benchmark set으로 구성해 실제 상담 환경을 충실히 반영했습니다.단, TelBench 데이터는 고객 개인정보를 보호하기 위해 모든 상담 기록에서 이름, 전화번호 같은 식별 정보를 제거한 후 사용합니다.모델이 학습 과정에서 민감한 내용을 흡수하지 않도록 대화 속 개인정보 표현을 다른 임의의 값으로 치환합니다.모델이 출력할 때도 프롬프트 설계로 개인정보를 재구성하지 못하게 하는 등 프라이버시 보호에 신경을 썼습니다.LLM 평가 결과(‘24)와 오픈소스 모델의 가능성작년 저희 팀은 TelBench를 통해 여러 상용 및 오픈소스 LLM을 평가했습니다.Claude 3.5 Sonnet이 전반적으로 가장 뛰어난 성능을 보였지만,Llama‑3.1‑405B‑Instruct‑FP8나 Mistral‑Large‑Instruct 같은 ‘24년 기준 최신 오픈소스 모델도 요약이나 감정 분류에서는 상용 모델과 비슷한 성능을 냈습니다.하지만 통신 지식이 많이 필요한 엔티티 인식과 의도 분류에서는 오픈소스 모델이 상용 모델보다 약 0.1~0.2 정도 낮은 성능을 보였습니다.이러한 결과는 “오픈소스 모델로 파인튜닝을 해봤지만 결과가 좋지 않았다”는 유럽 연구자의 경험과도 동일했습니다.도메인 지식이 필요한 과제에서는 아직 상용 모델이 앞서지만, 오픈소스 모델의 개선 속도가 빠르므로 장기적으로는 대안을 제공할 수 있을 것으로 전망했습니다.포스터 세션에서 주요 질문과 답변포스터 발표에서는 국내외 참가자들이 다양한 질문을 던졌습니다. 주요 내용은 다음과 같습니다.• None 영어 지원 계획: 현재 TelBench는 한국어 데이터만 포함하지만, 향후 영어를 포함한 다국어 지원을 계획하고 있습니다.• None 데이터 공개 여부: 사내 정책상 전체 데이터셋을 공개하지는 않으며, 논문 부록에 일부 샘플만 수록했습니다.• None 모델 사용 현황: Telco‑LLM은 현재 SKT 고객센터의 상담사 업무를 지원하며, 상담 내역 요약과 후속 조치(To‑Do) 생성에 활용됩니다.• None 연구 vs. 엔지니어링: 팀의 업무는 연구와 엔지니어링가 반반입니다. 데이터 구축, 모델 개발뿐 아니라 실제 서비스 배포까지 담당합니다.• None 다국어 모델 개발: 다국어 지원은 아직 초기 단계이며, 내년부터 본격적으로 추진할 계획이라고 밝혔습니다.• None 프라이버시 보호: 음성인식 이후 데이터를 가명화한 데이터를 사용하며, 프롬프트로 민감한 정보 추출을 방지합니다.학회 중에 ‘Language Agents: Foundations, Prospects and Risks’라는 튜토리얼 세션이 가장 인상 깊었습니다.이 세션은 LLM 기반 에이전트가 단순한 언어 모델의 래퍼가 아니라 “언어를 통해 추론과 의사소통을 수행하는 새로운 세대의 에이전트”라고 정의하며,Reasoning, Memory, Planning과 같은 기본 구성 요소부터 멀티 에이전트 시스템, 안전성 문제까지 폭넓게 다뤘습니다. (튜토리얼의 세부 내용은 따로 글을 써서 공유할 예정입니다.)개인적인 성찰과 앞으로의 계획EMNLP 2024 발표를 준비하면서 느낀 가장 큰 보람은 산업 현장의 문제를 해결하는 연구가 얼마나 큰 가치를 지니는지 확인한 것입니다.SK텔레콤 AI Data Engineering팀으로서, 상담사들의 업무 부담을 줄이고 서비스 품질을 향상시키는 도구를 만들기 위해 데이터 수집, 익명화, 모델 평가 등 다양한 도전을 해왔습니다.TelBench를 통해 우리는 텔코 도메인에서 필요한 과제를 명확히 정의하고, 객관적인 비교 기준을 제시할 수 있었습니다.학회 기간에 만난 여러 국내외 연구자와 엔지니어들이 “이런 데이터셋이 있었으면 좋겠다”는 공감을 보여준 것도 큰 힘이 되었습니다.한편 2025년 2월 GSMA가 ‘Open‑Telco LLM Benchmarks’라는 오픈 커뮤니티를 출범시키며,통신 도메인에서 LLM의 한계를 지적하고 투명한 벤치마크의 필요성을 강조했습니다(gsma-benchmarks).GPT‑4가 TeleQnA 데이터셋에서 75% 미만의 점수를
2025.07.29

좋아요

별로에요

코드로 쓰는 정책서
조직 내에서 정책이나 업무 기준을 파악할 때, 우리는 종종 오래된 문서를 뒤지거나 여러 사람에게 반복적으로 문의해야 하는 번거로움을 경험합니다. 이러한 방식은 답변이 지연될 뿐 아니라, 전달받은 정보가 부정확하거나 불완전한 경우가 많아 실제 업무에 혼선을 일으키곤 합니다. 특히 최신 정책이 반영되지 않은 문서에 의존하면, 중요한 의사결정 과정에서 누락이나 오류가 발생할 위험도 높아집니다.이런 현실 속에서, 정책 정보를 보다 신속하고 정확하게 전달하는 새로운 접근이 필요하다는 문제의식이 커지고 있습니다. 이번 실험은 코드에서 직접 정책을 추출하고, 최신 정보를 실시간으로 제공함으로써 반복적인 커뮤니케이션에 드는 시간과 노력을 크게 줄일 수 있음을 보여주었습니다. “문서는 오래되어 있고, 정책서도 최신 상태가 아니었죠.“라는 경험처럼, 이 실험이 조직의 민첩성을 높이는 중요한 출발점이 될 수 있음을 시사합니다.박으뜸찬솔님은 AI와 코딩 에이전트인 Cursor를 활용해 이러한 문제를 혁신적으로 해결할 수 있다는 가능성을 발견하고, 직접 실험에 나섰습니다. 반복적인 문의와 오래된 문서에 의존하는 기존의 커뮤니케이션 방식을 넘어, 코드에서 최신 정책을 추출하고 실시간으로 정보를 제공하는 새로운 방식을 시도한 것이죠. 이제 그 구체적인 여정을 같이 떠나 보시죠.플라이보드 프로젝트가 드러낸 현실여행 서비스를 개발하다 보면 고객 여정의 연결고리가 끊어지는 지점을 발견하게 되죠. 한 PM이 플라이보드 프로젝트를 진행하면서 마주한 상황도 그랬어요.“고객들은 항공편을 구매하고, 숙박도 예약하고, 투어 액티비티까지 다 구매하는데, 그 이후의 여정이 없어요. 고객은 거기서 여행이 끝나지 않았는데 저희는 그 이후에 뭔가 고객들의 액션이 없으니까 이것들을 계속 뭔가 고객여정의 끝까지 각구하기 위해서 더 고객들을 후킹해서 들어올 수 있게끔 하고자 했어요.”4일 만에 기획부터 개발, 릴리즈까지 완료한 플라이보드 프로젝트. 실제 개발 시간은 8시간에 불과했지만, 이 프로젝트를 실제 서비스에 붙이는 과정에서 예상치 못한 벽에 부딪히게 되었어요.“항공개발팀에서 되게 많은 도움을 주셨는데, 제가 사용자들을 늘리고 싶어서 인천공항으로 출발한 고객들에게 알림톡을 발송하는 것을 요청드렸어요. 근데 그분들의 배포주기와 코드 작업으로 인해서 저는 기다릴 수밖에 없었죠.”문제는 단순히 기다리는 것이 아니었어요. 정책을 파악하고, 룰을 이해하고, 타겟팅 기준을 명확히 하는 과정에서 계속 사람에게 물어봐야 하는 상황이 반복되었거든요. 문서는 오래되어 있고, 정책서도 최신 상태가 아니었죠.커서 룰로 AI 에이전트 길들이기그렇다면 가장 최신의 정책을 담고 있는 것은 무엇일까요? 바로 코드예요. 코드에서 직접 정책을 추출하는 실험이 시작된 이유죠.“결국 우리가 가장 최신 정책을 담고 있는 것은 모두 다 아실 거예요. 결국 코드예요. 그러면 그 코드 기반으로 정책을 뽑아낼 수 있는 방법은 없을까요?”커서(Cursor)라는 AI 코딩 도구를 활용했지만, 단순히 도구를 쓰는 것이 아니라 ‘룰(Rul
java
7/29/2025

코드로 쓰는 정책서
조직 내에서 정책이나 업무 기준을 파악할 때, 우리는 종종 오래된 문서를 뒤지거나 여러 사람에게 반복적으로 문의해야 하는 번거로움을 경험합니다. 이러한 방식은 답변이 지연될 뿐 아니라, 전달받은 정보가 부정확하거나 불완전한 경우가 많아 실제 업무에 혼선을 일으키곤 합니다. 특히 최신 정책이 반영되지 않은 문서에 의존하면, 중요한 의사결정 과정에서 누락이나 오류가 발생할 위험도 높아집니다.이런 현실 속에서, 정책 정보를 보다 신속하고 정확하게 전달하는 새로운 접근이 필요하다는 문제의식이 커지고 있습니다. 이번 실험은 코드에서 직접 정책을 추출하고, 최신 정보를 실시간으로 제공함으로써 반복적인 커뮤니케이션에 드는 시간과 노력을 크게 줄일 수 있음을 보여주었습니다. “문서는 오래되어 있고, 정책서도 최신 상태가 아니었죠.“라는 경험처럼, 이 실험이 조직의 민첩성을 높이는 중요한 출발점이 될 수 있음을 시사합니다.박으뜸찬솔님은 AI와 코딩 에이전트인 Cursor를 활용해 이러한 문제를 혁신적으로 해결할 수 있다는 가능성을 발견하고, 직접 실험에 나섰습니다. 반복적인 문의와 오래된 문서에 의존하는 기존의 커뮤니케이션 방식을 넘어, 코드에서 최신 정책을 추출하고 실시간으로 정보를 제공하는 새로운 방식을 시도한 것이죠. 이제 그 구체적인 여정을 같이 떠나 보시죠.플라이보드 프로젝트가 드러낸 현실여행 서비스를 개발하다 보면 고객 여정의 연결고리가 끊어지는 지점을 발견하게 되죠. 한 PM이 플라이보드 프로젝트를 진행하면서 마주한 상황도 그랬어요.“고객들은 항공편을 구매하고, 숙박도 예약하고, 투어 액티비티까지 다 구매하는데, 그 이후의 여정이 없어요. 고객은 거기서 여행이 끝나지 않았는데 저희는 그 이후에 뭔가 고객들의 액션이 없으니까 이것들을 계속 뭔가 고객여정의 끝까지 각구하기 위해서 더 고객들을 후킹해서 들어올 수 있게끔 하고자 했어요.”4일 만에 기획부터 개발, 릴리즈까지 완료한 플라이보드 프로젝트. 실제 개발 시간은 8시간에 불과했지만, 이 프로젝트를 실제 서비스에 붙이는 과정에서 예상치 못한 벽에 부딪히게 되었어요.“항공개발팀에서 되게 많은 도움을 주셨는데, 제가 사용자들을 늘리고 싶어서 인천공항으로 출발한 고객들에게 알림톡을 발송하는 것을 요청드렸어요. 근데 그분들의 배포주기와 코드 작업으로 인해서 저는 기다릴 수밖에 없었죠.”문제는 단순히 기다리는 것이 아니었어요. 정책을 파악하고, 룰을 이해하고, 타겟팅 기준을 명확히 하는 과정에서 계속 사람에게 물어봐야 하는 상황이 반복되었거든요. 문서는 오래되어 있고, 정책서도 최신 상태가 아니었죠.커서 룰로 AI 에이전트 길들이기그렇다면 가장 최신의 정책을 담고 있는 것은 무엇일까요? 바로 코드예요. 코드에서 직접 정책을 추출하는 실험이 시작된 이유죠.“결국 우리가 가장 최신 정책을 담고 있는 것은 모두 다 아실 거예요. 결국 코드예요. 그러면 그 코드 기반으로 정책을 뽑아낼 수 있는 방법은 없을까요?”커서(Cursor)라는 AI 코딩 도구를 활용했지만, 단순히 도구를 쓰는 것이 아니라 ‘룰(Rul
2025.07.29
java

좋아요

별로에요