logo
emoji
검색하기
어제 가장 많이 검색된 기술
emoji
가장 많이 읽은 글
logo
HashiCorp Vault를 활용한 SSH 인증서 기반 서버 접속 구현
HashiCorp Vault는 민감한 자격 증명, API 키, 암호화 키 등을 안전하게 저장하고 접근 제어를 제공하는 비밀 관리 시스템입니다.그중에서도 SSH Secrets Engine은 SSH 접속을 보다 안전하게 제어할 수 있도록 지원하는 기능으로,OTP 방식 또는 SSH 인증서 서명 방식으로 서버 접근을 중앙에서 관리할 수 있습니다.이번 포스팅에서는 Vault에 SSH Secrets Engine을 SSH 인증서 방식으로 서버에 접근하는 방법에 대한 내용을 다룹니다.• None Vault 웹 UI 활성화(ui) 및 Vault를 외부에서 호출 가능한 API 주소(api_addr) 설정• None Storage는 Vault의 데이터 저장 방식이고, 여기에서는 /opt/vault/data 디렉토리에 저장• None listener는 TCP listener에서 사용하는 서비스 Port 및 Vault 서버가 어떤 IP 주소로 요청을 받을지 지정 tls-disable은 tls를 비화성화 하는 것이고, 실제 상용에서는 활성화해서 사용 필요.• None Vault 서버를 위에서 작성한 환경 설정을 통해서 서버 구동• None 본 포스팅에서는 백그라운드로 실행하는 것으로 진행.• None Unseal Key와 관련하여, -key-shares, -key-threshold 옵션 사용 가능하며, 옵션은 내용은 다음과 같습니다.• None -key-threshold : 생성된 Unseal 키 중에서, Vault를 Unseal 하는 데 사용하는 키 수 (default 3)• None Vault init 이후, sealed 상태로, Vault 사용을 위해서 unseal을 수행• None unseal은 -key-threshold에 선언된 만큼 진행해야 하면, unseal된 수량은 'Unseal Progress'에서 확인 가능• None 별도 옵션이 없을 경우, -key-threshold=3 으로 되어서, 3번의 unseal 수행• None unseal이 완료되면, 'Sealed'가 'true'에서 'false'로 변경 됨.• None 별도 로그인 대신에, VAULT_TOKEN 환경 변수에 token 값을 선언해서 사용하는 것도 가능• None secrets enable로 Secret Engine을 활성화합니다.• None -path 옵션을 이용해서, 마운트 지점을 설정하는 데, 이는 SSH Secret Engine이 사용할 논리 경로• None -path 옵션을 사용해서, 서로 다른 설정을 여러 번 마운트 가능 예를 들어서, 이번 포스팅에서 다루는 SSH 인증서 방식과 다음 포스팅에서 다룰 OTP 인증서 방식을 각각 구성 가능- Vault가 내부적으로 사용할 SSH 인증서 서명용 키쌍 (private/public) 을 자동으로 생성- 이 키는 Vault가 클라이언트의 공개 키를 받아 SSH 인증서 형식으로 서명할 때 사용- Vault는 자체적으로 SSH CA 역할을 수행하게 되며,이후 발급된 인증서는 대상 서버의 TrustedUserCAKeys에 등록된 공개 키와 비교되어 접근을 제어• None ssh-client-signer/config/ca 경로의 리소스(공개 키 포함)에 대해 읽기 권한만 허용SSH Public Key Read를 위한 Token 생성(SSH Server에서 사용)• None 앞서 만든 ssh-pubkey-read 정책을 받은 전용 토큰 생성• None 이 토큰을 사용해서, SSH Server가 Vault에서 서명된 CA 공개키를 읽을 수 있음SSH-Client가 Vault에 CA 사인 요청을 할 수 있도록 설정• None 클라이언트가 제출한 공개 키를 받아 인증서(서명된 키)로 발급해주는 기능을 제공• None 기본 계정을 zigi로 설정하고, 인증서에 대한 TTL을 5분으로 지정• None SSH Client가 앞서 생성한 역할(role)을 통해 인증서 서명을 요청하기 위한 sign 경로의 update 권한 정의인증서 서명을 위한 Token 생성(SSH Client에서 사용)• None 앞서 만든 require-ssh-sign 정책을 받은 전용 토큰 생성• None 이 토큰을 사용해서, SSH Client가 Vault에 인증서 서명 요청Vault에서 서명한 SSH 인증서를 신뢰하기 위해서, Vault의 CA 공개키를 가져와서 등록• None VAULT Token 값은 'SSH Public Key Read를 위한 Token 생성(SSH Server에서 사용)' 에서 확인한 Token 값 사용CA의 PublicKey를 SSH-Server에 추가• None Vault에서 Public Key를 SSH 서버로 가져와서, SSH 서버에 저장Public Key 경로를 SSH 구성 파일에 TrustedUserCAKeys 옵션으로 추가• None OpenSSH에서 서명된 인증서를 신뢰하기 위한 CA 공개키로 지정SSH 클라이언트는 자신의 공개키를 Vault에 제출하여 서명된 인증서를 발급받고, 이를 통해 SSH 서버에 안전하게 로그인합니다.• None VAULT Token 값은 ' 인증서 서명을 위한 Token 생성(SSH Client에서 사용) ' 단계에서 생성한 Token.• None 생성한 Public Key를 Vault에 제출하고, 기존에 생성한 역할(role) 기반으로 서명된 SSH 인증서 발급• None -i 옵션으로 앞서 생성한 Private Key를 지정해서, SSH Server에 접속• None OpenSSH가 같은 경로에 앞서 발급 받은 SSH 인증서를 함께 사용• None SSH Server에서는 TrustedUserCAKeys 값을 기준으로 서명된 인증서를 신뢰
vault
8/8/2025
logo
HashiCorp Vault를 활용한 SSH 인증서 기반 서버 접속 구현
HashiCorp Vault는 민감한 자격 증명, API 키, 암호화 키 등을 안전하게 저장하고 접근 제어를 제공하는 비밀 관리 시스템입니다.그중에서도 SSH Secrets Engine은 SSH 접속을 보다 안전하게 제어할 수 있도록 지원하는 기능으로,OTP 방식 또는 SSH 인증서 서명 방식으로 서버 접근을 중앙에서 관리할 수 있습니다.이번 포스팅에서는 Vault에 SSH Secrets Engine을 SSH 인증서 방식으로 서버에 접근하는 방법에 대한 내용을 다룹니다.• None Vault 웹 UI 활성화(ui) 및 Vault를 외부에서 호출 가능한 API 주소(api_addr) 설정• None Storage는 Vault의 데이터 저장 방식이고, 여기에서는 /opt/vault/data 디렉토리에 저장• None listener는 TCP listener에서 사용하는 서비스 Port 및 Vault 서버가 어떤 IP 주소로 요청을 받을지 지정 tls-disable은 tls를 비화성화 하는 것이고, 실제 상용에서는 활성화해서 사용 필요.• None Vault 서버를 위에서 작성한 환경 설정을 통해서 서버 구동• None 본 포스팅에서는 백그라운드로 실행하는 것으로 진행.• None Unseal Key와 관련하여, -key-shares, -key-threshold 옵션 사용 가능하며, 옵션은 내용은 다음과 같습니다.• None -key-threshold : 생성된 Unseal 키 중에서, Vault를 Unseal 하는 데 사용하는 키 수 (default 3)• None Vault init 이후, sealed 상태로, Vault 사용을 위해서 unseal을 수행• None unseal은 -key-threshold에 선언된 만큼 진행해야 하면, unseal된 수량은 'Unseal Progress'에서 확인 가능• None 별도 옵션이 없을 경우, -key-threshold=3 으로 되어서, 3번의 unseal 수행• None unseal이 완료되면, 'Sealed'가 'true'에서 'false'로 변경 됨.• None 별도 로그인 대신에, VAULT_TOKEN 환경 변수에 token 값을 선언해서 사용하는 것도 가능• None secrets enable로 Secret Engine을 활성화합니다.• None -path 옵션을 이용해서, 마운트 지점을 설정하는 데, 이는 SSH Secret Engine이 사용할 논리 경로• None -path 옵션을 사용해서, 서로 다른 설정을 여러 번 마운트 가능 예를 들어서, 이번 포스팅에서 다루는 SSH 인증서 방식과 다음 포스팅에서 다룰 OTP 인증서 방식을 각각 구성 가능- Vault가 내부적으로 사용할 SSH 인증서 서명용 키쌍 (private/public) 을 자동으로 생성- 이 키는 Vault가 클라이언트의 공개 키를 받아 SSH 인증서 형식으로 서명할 때 사용- Vault는 자체적으로 SSH CA 역할을 수행하게 되며,이후 발급된 인증서는 대상 서버의 TrustedUserCAKeys에 등록된 공개 키와 비교되어 접근을 제어• None ssh-client-signer/config/ca 경로의 리소스(공개 키 포함)에 대해 읽기 권한만 허용SSH Public Key Read를 위한 Token 생성(SSH Server에서 사용)• None 앞서 만든 ssh-pubkey-read 정책을 받은 전용 토큰 생성• None 이 토큰을 사용해서, SSH Server가 Vault에서 서명된 CA 공개키를 읽을 수 있음SSH-Client가 Vault에 CA 사인 요청을 할 수 있도록 설정• None 클라이언트가 제출한 공개 키를 받아 인증서(서명된 키)로 발급해주는 기능을 제공• None 기본 계정을 zigi로 설정하고, 인증서에 대한 TTL을 5분으로 지정• None SSH Client가 앞서 생성한 역할(role)을 통해 인증서 서명을 요청하기 위한 sign 경로의 update 권한 정의인증서 서명을 위한 Token 생성(SSH Client에서 사용)• None 앞서 만든 require-ssh-sign 정책을 받은 전용 토큰 생성• None 이 토큰을 사용해서, SSH Client가 Vault에 인증서 서명 요청Vault에서 서명한 SSH 인증서를 신뢰하기 위해서, Vault의 CA 공개키를 가져와서 등록• None VAULT Token 값은 'SSH Public Key Read를 위한 Token 생성(SSH Server에서 사용)' 에서 확인한 Token 값 사용CA의 PublicKey를 SSH-Server에 추가• None Vault에서 Public Key를 SSH 서버로 가져와서, SSH 서버에 저장Public Key 경로를 SSH 구성 파일에 TrustedUserCAKeys 옵션으로 추가• None OpenSSH에서 서명된 인증서를 신뢰하기 위한 CA 공개키로 지정SSH 클라이언트는 자신의 공개키를 Vault에 제출하여 서명된 인증서를 발급받고, 이를 통해 SSH 서버에 안전하게 로그인합니다.• None VAULT Token 값은 ' 인증서 서명을 위한 Token 생성(SSH Client에서 사용) ' 단계에서 생성한 Token.• None 생성한 Public Key를 Vault에 제출하고, 기존에 생성한 역할(role) 기반으로 서명된 SSH 인증서 발급• None -i 옵션으로 앞서 생성한 Private Key를 지정해서, SSH Server에 접속• None OpenSSH가 같은 경로에 앞서 발급 받은 SSH 인증서를 함께 사용• None SSH Server에서는 TrustedUserCAKeys 값을 기준으로 서명된 인증서를 신뢰
2025.08.08
vault
emoji
좋아요
emoji
별로에요
logo
3년 차 앱 개발자가 일하는 순서를 공유합니다
안녕하세요, LINE Plus에서 근무하고 있는 최연두입니다. 이 글에서는 시행착오 끝에 정착한 저만의 ‘일하는 방식’을 공유하려고 합니다. 다른 팀의 아무개가 일하는 방식이 궁금하신 분, 효율적인 협업과 코드 리뷰에 관심 있는 분들께 도움이 되길 바랍니다. 참고로 이 글은 작업 관리에 Jira를 사용한다고 전제하고 관련 개념과 용어를 사용하고 있습니다. 혹시 관련 개념과 용어가 생소하신 분들은 Jira Docs를 참고해 주시기를 부탁드리며 시작하겠습니다.저는 효율적이고 실수 없는 개발, 그리고 원활한 리뷰를 위해 이런 프로세스를 정립했습니다.• 기획서 리뷰 및 머릿속으로 기능 시뮬레이션• 작업 흐름을 Jira 티켓 등으로 가시화• 티켓에 대해 PoC(proof of concept)/프로토타이핑 진행제가 정립한 프로세스를 각 단계별로 살펴보겠습니다."시작은 전체 흐름과 요구 사항을 명확히 이해하는 것에서부터!"기획서를 꼼꼼히 읽어 기능의 목적과 작동 방식을 이해합니다. 이해가 안 되는 부분이 있거나 사용자 경험을 위해 더 나은 아이디어가 떠오르면, 기획자와 소통합니다.기능이 작동하는 모습을 시뮬레이션할 때에는 실제 코드를 짠다고 상상하며 데이터 흐름과 화면 전환, 예외 상황은 어떻게 발생할지 시뮬레이션합니다. 예를 들어 각자가 사용하는 상태 관리 기법에 따라서 에러 핸들링을 처리해야 할 수도 있고 하지 않아야 할 수도 있습니다."전체 흐름을 그리고 세부 작업을 만들어 보세요."개발할 기능을 어떤 흐름으로 구현할지 간단히 기능의 흐름을 그려봅니다. 다이어그램이어도 되고 단순하게 화살표를 이용해도 좋습니다.다음으로 데이터가 어디에서 오고 어떤 처리가 필요한지, 이벤트에 따라 상태가 어떻게 변할지 생각해 봅니다. 앞서 작성한 기능의 흐름도를 보며 하나의 주제로 엮일만한 것끼리 묶어 작업을 만듭니다.마지막으로 지금까지 그린 기능의 흐름을 새로 정리한다는 마음으로, 전체 구조와 데이터 흐름을 간단한 다이어그램으로 표현합니다. 너무 정교할 필요는 없습니다. 앞서 만든 작업이 연결되도록 주요 단계별로 묶어서 큰 그림을 그려보세요.작업 흐름을 Jira 티켓 등으로 가시화"전체 작업을 눈에 보이게 쪼개서 관리하세요."앞서 그린 큰 단위의 작업 흐름을 바탕으로, 각 작업을 Jira 티켓(또는 사용하는 툴)에 등록해 구체적으로 관리합니다. 저는 기획서 전체를 하나의 에픽(epic)으로 만들고 그 하위에 여러 이슈를 생성했는데요. 아래는 그중 일부를 예시로 가져온 것입니다.참고로 이 시점에는 티켓의 순서는 상관없습니다."내 예상대로 작동하는지, 작업의 크기는 적절한지 점검해 보세요."이제 PoC나 프로토타입을 만들어 보면서 앞서 생성한 티켓에 혹시 빠지거나 중복된 부분은 없는지, 예상치 못한 문제나 시나리오는 없는지 확인합니다.완성된 결과물을 GitHub에 올려 작업량 혹은 변경 사항이 얼마나 되는지 확인해 봅시다. 양이 많다면 주제에 따라서 티켓을 유지 혹은 분리하거나 부작업으로 세분화할지 고민해야 합니다. PoC이기 때문에 테스트 코드도 없고 문서화를 위한 주석도 없습니다. 따라서 온전히 기능만을 위해 수정된 코드가 몇 줄인지 확인할 수 있는데요. 제 경우 이때 코드가 400줄을 넘어가면 작업량이 많거나 변경 사항의 규모가 크다고 판단합니다.변경 사항의 규모가 크다고 판단한 경우, 먼저 코드에 여러 주제가 섞여 있는지 확인합니다. 만약 그렇다면 주제별로 작업을 분리하는 것이 좋습니다. 그렇지 않고 한 가지 주제라면, Jira 티켓의 하위 작업(sub-task) 기능을 활용해 세분화합니다. 그 외의 경우에는 티켓을 혀재 상태로 유지하면 됩니다.예를 들어 저의 경우, 앞서 예시 이미지 속 'Implement the scheduled stockout & sale status page'는 PoC 이후 아래와 같이 세분화됐습니다.그런데 왜 그냥 하나의 티켓으로 관리하지 않고 이와 같이 티켓을 분리하거나 세분화하는 과정에 시간과 에너지를 사용해야 할까요?제가 속한 팀은 코드 리뷰를 중요하게 여기고 있습니다. 따라서 리뷰하기 쉬워야 작업이 머지되기 쉬운데요. 리뷰하기 쉬운 작업이란 다음과 같은 작업을 의미합니다.• 말하고자 하는 바가 명확한 작업위와 같은 조건을 갖추려면 규모가 큰 변경의 경우 적절한 크기로 티켓을 나누는 게 필요합니다. 즉, 티켓 크기 조정 단계는 저희 팀의 리뷰 문화에 입각해서 도출된 과정이라고 할 수 있습니다. 따라서 이 단계는 각자의 팀 상황에 맞춰 적절히 조절하며 진행하면 좋을 것 같습니다."작고 명확한 커밋, 이해하기 쉬운 코드로 리뷰어의 시간을 아껴주세요."앞서의 과정은 바로 이 단계를 위한 과정이었다고 해도 과언이 아닙니다. 앞선 단계를 잘 진행해 오셨다면 티켓의 크기는 리뷰어가 리뷰하기 좋은 크기로 나뉘어 있을 것입니다. 따라서 작업의 주제에 맞춰 작업을 구현해 주세요.다만 작업을 구현할 때에는 다음 사항에 유의해 주세요.• 커밋 단위는 너무 크지 않게(의미별로 쪼개기)• 코드의 변경 순서도 논리적으로(예: 인터페이스 → 구현)작업하다 보면 '이 정도로 리뷰어를 배려해야 하나?'라는 생각이 드실 수도 있는데요. 이런 접근 방식은 아래와 같은 장점에 힘입어 나에게도 도움이 됩니다.• 일하는 방식이 구체화되면서 작업이 예측 가능해집니다.• 히스토리를 파악하는 데 도움이 됩니다.• 작은 단위이기 때문에 리버트(revert)가 필요한 경우 결정 및 실행이 쉽습니다.“PR을 작성할 때 리뷰어가 이해하기 쉽게 문서화한다고 생각해 보세요.”PR을 만들 때는 이 PR의 목적이 무엇인지 명확히 밝히고, 리뷰어가 알아야 할 사전 지식과 이슈, 테스트 방법 등을 상세히 적습니다. 만약 테스트하는 방법이 복잡한 PR이라면 작동 영상을 첨부하는 것도 좋은 방법입니다.다음은 제가 사용하고 있는 PR 템플릿입니다.이 프로세스에서 동료와 작업의 방향을 맞추는 시점은 언제가 적당할까요?“혼자 고민하지 말고, 동료와 미리 의견을 나누면 리뷰 시간이 짧아집니다.”새로운 컴포넌트를 개발한다거나 여러 명이 관여하는 작업을 진행할 때에는 작업 흐름을 Jira 티켓으로 만들기 전에 간단하게 동료에게 설명하고
jira
8/8/2025
logo
3년 차 앱 개발자가 일하는 순서를 공유합니다
안녕하세요, LINE Plus에서 근무하고 있는 최연두입니다. 이 글에서는 시행착오 끝에 정착한 저만의 ‘일하는 방식’을 공유하려고 합니다. 다른 팀의 아무개가 일하는 방식이 궁금하신 분, 효율적인 협업과 코드 리뷰에 관심 있는 분들께 도움이 되길 바랍니다. 참고로 이 글은 작업 관리에 Jira를 사용한다고 전제하고 관련 개념과 용어를 사용하고 있습니다. 혹시 관련 개념과 용어가 생소하신 분들은 Jira Docs를 참고해 주시기를 부탁드리며 시작하겠습니다.저는 효율적이고 실수 없는 개발, 그리고 원활한 리뷰를 위해 이런 프로세스를 정립했습니다.• 기획서 리뷰 및 머릿속으로 기능 시뮬레이션• 작업 흐름을 Jira 티켓 등으로 가시화• 티켓에 대해 PoC(proof of concept)/프로토타이핑 진행제가 정립한 프로세스를 각 단계별로 살펴보겠습니다."시작은 전체 흐름과 요구 사항을 명확히 이해하는 것에서부터!"기획서를 꼼꼼히 읽어 기능의 목적과 작동 방식을 이해합니다. 이해가 안 되는 부분이 있거나 사용자 경험을 위해 더 나은 아이디어가 떠오르면, 기획자와 소통합니다.기능이 작동하는 모습을 시뮬레이션할 때에는 실제 코드를 짠다고 상상하며 데이터 흐름과 화면 전환, 예외 상황은 어떻게 발생할지 시뮬레이션합니다. 예를 들어 각자가 사용하는 상태 관리 기법에 따라서 에러 핸들링을 처리해야 할 수도 있고 하지 않아야 할 수도 있습니다."전체 흐름을 그리고 세부 작업을 만들어 보세요."개발할 기능을 어떤 흐름으로 구현할지 간단히 기능의 흐름을 그려봅니다. 다이어그램이어도 되고 단순하게 화살표를 이용해도 좋습니다.다음으로 데이터가 어디에서 오고 어떤 처리가 필요한지, 이벤트에 따라 상태가 어떻게 변할지 생각해 봅니다. 앞서 작성한 기능의 흐름도를 보며 하나의 주제로 엮일만한 것끼리 묶어 작업을 만듭니다.마지막으로 지금까지 그린 기능의 흐름을 새로 정리한다는 마음으로, 전체 구조와 데이터 흐름을 간단한 다이어그램으로 표현합니다. 너무 정교할 필요는 없습니다. 앞서 만든 작업이 연결되도록 주요 단계별로 묶어서 큰 그림을 그려보세요.작업 흐름을 Jira 티켓 등으로 가시화"전체 작업을 눈에 보이게 쪼개서 관리하세요."앞서 그린 큰 단위의 작업 흐름을 바탕으로, 각 작업을 Jira 티켓(또는 사용하는 툴)에 등록해 구체적으로 관리합니다. 저는 기획서 전체를 하나의 에픽(epic)으로 만들고 그 하위에 여러 이슈를 생성했는데요. 아래는 그중 일부를 예시로 가져온 것입니다.참고로 이 시점에는 티켓의 순서는 상관없습니다."내 예상대로 작동하는지, 작업의 크기는 적절한지 점검해 보세요."이제 PoC나 프로토타입을 만들어 보면서 앞서 생성한 티켓에 혹시 빠지거나 중복된 부분은 없는지, 예상치 못한 문제나 시나리오는 없는지 확인합니다.완성된 결과물을 GitHub에 올려 작업량 혹은 변경 사항이 얼마나 되는지 확인해 봅시다. 양이 많다면 주제에 따라서 티켓을 유지 혹은 분리하거나 부작업으로 세분화할지 고민해야 합니다. PoC이기 때문에 테스트 코드도 없고 문서화를 위한 주석도 없습니다. 따라서 온전히 기능만을 위해 수정된 코드가 몇 줄인지 확인할 수 있는데요. 제 경우 이때 코드가 400줄을 넘어가면 작업량이 많거나 변경 사항의 규모가 크다고 판단합니다.변경 사항의 규모가 크다고 판단한 경우, 먼저 코드에 여러 주제가 섞여 있는지 확인합니다. 만약 그렇다면 주제별로 작업을 분리하는 것이 좋습니다. 그렇지 않고 한 가지 주제라면, Jira 티켓의 하위 작업(sub-task) 기능을 활용해 세분화합니다. 그 외의 경우에는 티켓을 혀재 상태로 유지하면 됩니다.예를 들어 저의 경우, 앞서 예시 이미지 속 'Implement the scheduled stockout & sale status page'는 PoC 이후 아래와 같이 세분화됐습니다.그런데 왜 그냥 하나의 티켓으로 관리하지 않고 이와 같이 티켓을 분리하거나 세분화하는 과정에 시간과 에너지를 사용해야 할까요?제가 속한 팀은 코드 리뷰를 중요하게 여기고 있습니다. 따라서 리뷰하기 쉬워야 작업이 머지되기 쉬운데요. 리뷰하기 쉬운 작업이란 다음과 같은 작업을 의미합니다.• 말하고자 하는 바가 명확한 작업위와 같은 조건을 갖추려면 규모가 큰 변경의 경우 적절한 크기로 티켓을 나누는 게 필요합니다. 즉, 티켓 크기 조정 단계는 저희 팀의 리뷰 문화에 입각해서 도출된 과정이라고 할 수 있습니다. 따라서 이 단계는 각자의 팀 상황에 맞춰 적절히 조절하며 진행하면 좋을 것 같습니다."작고 명확한 커밋, 이해하기 쉬운 코드로 리뷰어의 시간을 아껴주세요."앞서의 과정은 바로 이 단계를 위한 과정이었다고 해도 과언이 아닙니다. 앞선 단계를 잘 진행해 오셨다면 티켓의 크기는 리뷰어가 리뷰하기 좋은 크기로 나뉘어 있을 것입니다. 따라서 작업의 주제에 맞춰 작업을 구현해 주세요.다만 작업을 구현할 때에는 다음 사항에 유의해 주세요.• 커밋 단위는 너무 크지 않게(의미별로 쪼개기)• 코드의 변경 순서도 논리적으로(예: 인터페이스 → 구현)작업하다 보면 '이 정도로 리뷰어를 배려해야 하나?'라는 생각이 드실 수도 있는데요. 이런 접근 방식은 아래와 같은 장점에 힘입어 나에게도 도움이 됩니다.• 일하는 방식이 구체화되면서 작업이 예측 가능해집니다.• 히스토리를 파악하는 데 도움이 됩니다.• 작은 단위이기 때문에 리버트(revert)가 필요한 경우 결정 및 실행이 쉽습니다.“PR을 작성할 때 리뷰어가 이해하기 쉽게 문서화한다고 생각해 보세요.”PR을 만들 때는 이 PR의 목적이 무엇인지 명확히 밝히고, 리뷰어가 알아야 할 사전 지식과 이슈, 테스트 방법 등을 상세히 적습니다. 만약 테스트하는 방법이 복잡한 PR이라면 작동 영상을 첨부하는 것도 좋은 방법입니다.다음은 제가 사용하고 있는 PR 템플릿입니다.이 프로세스에서 동료와 작업의 방향을 맞추는 시점은 언제가 적당할까요?“혼자 고민하지 말고, 동료와 미리 의견을 나누면 리뷰 시간이 짧아집니다.”새로운 컴포넌트를 개발한다거나 여러 명이 관여하는 작업을 진행할 때에는 작업 흐름을 Jira 티켓으로 만들기 전에 간단하게 동료에게 설명하고
2025.08.08
jira
emoji
좋아요
emoji
별로에요
logo
수식없이 GPT(트랜스포머) 이해하기. 1편
이번 포스팅은 GPT의 근간이 되는 트랜스포머의 이 어떤 구조로 되어있는지 간단히(?) 알아봅니다.현재 LLM모델을 Transformer를 Base로Transformer의 Decoder만을 사용해서 구현되고 있습니다.1 . Text가 들어가서 을 거치면 단어가 d차원의 Vector로 바뀐다.2 . d차원의 Vector가 으로 들어가고, 그 결과가 기존 값이랑 합쳐진다4 . 를 진행 시켜서 차원을 확장시키고, 다시 축소시켜 줍니다.결국 중요한건, (이후 Self Att블록) 블록의 Input과 Ouput의 Tensor의 크기 변화가 없다는 점 입니다.이제 GPT의 핵심중 하나인 Self Attention의 흐름을 같이 따라가 보겠습니다.다음에 어떤 단어가 올지를 찾는 과정이 되겠습니다.단어들이 각각 Vector로 변환되고, 위치 인코딩이 추가된 다음 (단어개수(=Seq), 임베딩 차원 d) 모양의 Matrix가 Block의 Input이 됩니다.이제 input x를 가지고 3개의 Latent Matrix를 만들게 됩니다.각각 Matrix의 이름을 Query, Key, Value라고 이름을 붙여줍니다.현재 보면 Shape이 (단어개수 x 임베딩 d) ==> (단어개수 x Latent d)로 변경되었습니다.Query, Key, Value가 특별한 Matrix같지만 사실상 기존 Input X를 다른 차원에 투영한 Matrix에 불과합니다.즉, QKV는 각각 단어들의 특징을 Latent d개의 Vector에 압축하고있다고 할 수 있습니다.Query와 Key간의 내적연산(두 Matrix간의 행렬곱)을 통해서 단어들 간의 얼마나 관계가 있는지 찾아냅니다.문제는 만 가지고 를 예측하는 단계에서, , 와 같이 나오지 않은 단어와의 연관성도 찾아볼 경우 치팅이 되기때문에 이부분을 가려서 을 진행하게 됩니다.이제 이 Masked Attention Pattern과 Value를 행렬곱을 취해서, 단어들의 최종 Attention Score를 구해 줍니다.문제는 보면 이러한 Attention이 하나만 있는게 아니라, 이라고 하여 위와같은 Attention이 한개 이상의 을 진행하게 됩니다.분명 Input X와 Output이 동일해야 한다고 했습니다. 하지만 현재는 동일하지가 않죠?그래서 출력된 3개(Head 개수)를 Concat하고, 이 Attention을 Input X와 동일한 차원으로 맞춰주게 됩니다.FeedForward Layer의 경우 모델의 비선형성을 추가해서 좀더 학습이 잘되게 만든다 라고 되어있습니다.근데 사실 이게 정확히 어떤 역할을 한다기 보다, 이걸 넣어보니깐 좀더 학습이 잘 되더라 Blcok 정도로 생각하면 좋습니다.먼저 Input X를 행렬곱을 통해서 Matrix의 크기를 키워줍니다.그리고, 다시 행렬곱을 통해서 Matrix를 축소시켜주는 열할을 합니다.이렇게 많은 수의 Weight를 모델에 추가하면서Input과 output Matrix의 구조가 동일한 Transformer Based LLM Model이 만들어지게 됩니다.이제 마지막에 Output X를 가지고 Classification을 통해서 다음 단어가 무엇이 나올지를 예측하게 됩니다Final X의 마지막 Row를 통해서 다음 단어를 예측 하게 됩니다.전체 흐름을 보면 이렇게 흘러가게 됩니다.
8/7/2025
logo
수식없이 GPT(트랜스포머) 이해하기. 1편
이번 포스팅은 GPT의 근간이 되는 트랜스포머의 이 어떤 구조로 되어있는지 간단히(?) 알아봅니다.현재 LLM모델을 Transformer를 Base로Transformer의 Decoder만을 사용해서 구현되고 있습니다.1 . Text가 들어가서 을 거치면 단어가 d차원의 Vector로 바뀐다.2 . d차원의 Vector가 으로 들어가고, 그 결과가 기존 값이랑 합쳐진다4 . 를 진행 시켜서 차원을 확장시키고, 다시 축소시켜 줍니다.결국 중요한건, (이후 Self Att블록) 블록의 Input과 Ouput의 Tensor의 크기 변화가 없다는 점 입니다.이제 GPT의 핵심중 하나인 Self Attention의 흐름을 같이 따라가 보겠습니다.다음에 어떤 단어가 올지를 찾는 과정이 되겠습니다.단어들이 각각 Vector로 변환되고, 위치 인코딩이 추가된 다음 (단어개수(=Seq), 임베딩 차원 d) 모양의 Matrix가 Block의 Input이 됩니다.이제 input x를 가지고 3개의 Latent Matrix를 만들게 됩니다.각각 Matrix의 이름을 Query, Key, Value라고 이름을 붙여줍니다.현재 보면 Shape이 (단어개수 x 임베딩 d) ==> (단어개수 x Latent d)로 변경되었습니다.Query, Key, Value가 특별한 Matrix같지만 사실상 기존 Input X를 다른 차원에 투영한 Matrix에 불과합니다.즉, QKV는 각각 단어들의 특징을 Latent d개의 Vector에 압축하고있다고 할 수 있습니다.Query와 Key간의 내적연산(두 Matrix간의 행렬곱)을 통해서 단어들 간의 얼마나 관계가 있는지 찾아냅니다.문제는 만 가지고 를 예측하는 단계에서, , 와 같이 나오지 않은 단어와의 연관성도 찾아볼 경우 치팅이 되기때문에 이부분을 가려서 을 진행하게 됩니다.이제 이 Masked Attention Pattern과 Value를 행렬곱을 취해서, 단어들의 최종 Attention Score를 구해 줍니다.문제는 보면 이러한 Attention이 하나만 있는게 아니라, 이라고 하여 위와같은 Attention이 한개 이상의 을 진행하게 됩니다.분명 Input X와 Output이 동일해야 한다고 했습니다. 하지만 현재는 동일하지가 않죠?그래서 출력된 3개(Head 개수)를 Concat하고, 이 Attention을 Input X와 동일한 차원으로 맞춰주게 됩니다.FeedForward Layer의 경우 모델의 비선형성을 추가해서 좀더 학습이 잘되게 만든다 라고 되어있습니다.근데 사실 이게 정확히 어떤 역할을 한다기 보다, 이걸 넣어보니깐 좀더 학습이 잘 되더라 Blcok 정도로 생각하면 좋습니다.먼저 Input X를 행렬곱을 통해서 Matrix의 크기를 키워줍니다.그리고, 다시 행렬곱을 통해서 Matrix를 축소시켜주는 열할을 합니다.이렇게 많은 수의 Weight를 모델에 추가하면서Input과 output Matrix의 구조가 동일한 Transformer Based LLM Model이 만들어지게 됩니다.이제 마지막에 Output X를 가지고 Classification을 통해서 다음 단어가 무엇이 나올지를 예측하게 됩니다Final X의 마지막 Row를 통해서 다음 단어를 예측 하게 됩니다.전체 흐름을 보면 이렇게 흘러가게 됩니다.
2025.08.07
emoji
좋아요
emoji
별로에요
logo
EMNLP24 늦은 후기 2탄: CC 데이터로 LLM 사전학습 데이터셋을 만들어본 경험 및 NVIDIA 논문 리뷰
LLM 시대가 오기 전에 SK텔레콤에서는 일종의 Moon shot 프로젝트를 진행한 적이 있습니다.지금은 LLM(Large Language Model)이라는 이름이 당연하지만 당시에는 GLM(General-Purpose Language Model)이라고 불렸죠.사전학습 모델을 위한 대규모 말뭉치가 필요했지만, 얼마나 필요한지도 몰랐고, 당연하게도 아무런 기반이 없었습니다.그래서 데이터 엔지니어링을 담당했던 저와 동료들은 처음부터 데이터를 모으고 처리하는 파이프라인을 기획하고, PySpark를 이용해 뼈대를 개발했습니다.CCNet: Extracting High Quality Monolingual Datasets from Web Crawl Data 논문을 참고하여 웹 크롤링 데이터에서 한국어 문서를 모으고 선별하는 방식으로 구축했습니다.작년 EMNLP 참가했을 때, 워크숍 세션에서 엔비디아 연구팀은 Pretrain 용 데이터 구축 방법에 대한 노하우를 연구 성과로 발표했습니다.우리 팀에서 과거에 매 과정마다 시행착오를 거치면서 알게 되었던 내용이 체계적으로 정리되어 있어서 크게 반가웠고 많이 공감했습니다.이 글에서는 당시의 좌충우돌 경험을 회상하며 Pretrain Dataset 구축 방법(엔비디아 논문)을 소개해 보려 합니다.CCNet 기반 데이터 파이프라인 구축데이터 구축을 위해 병렬처리가 필요한데, Spark나 Dask도 고민은 했었지만 개발 용이성과 성능 이점 모두를 고려해봤을 때 PySpark가 최선의 선택이라고 생각했습니다.일정 단위로 파이프라인을 스케줄링하기 위해서는 Airflow를 이용했고요. 당시에는 대규모 데이터를 다룰 경험이 많지 않아 매 단계가 도전이었습니다.CCNet 구조를 토대로 기본적인 데이터 흐름을 만들었지만, 실제 적용 과정에서 많은 문제에 부딪혔습니다.그 중 가장 기억에 남는 세가지를 소개합니다.Common Crawl 데이터셋 처럼 웹에서 긁어온 문서는 품질 편차가 매우 큽니다. 그래서 웹 크롤링 데이터를 활용할 때 가장 처음 맞닥뜨리는 문제는 데이터의 질 관리입니다.그래서 CCNet 논문에서도 위키백과 등 고품질 코퍼스만으로 학습한 언어 모델을 사용해 perplexity metric로 문서를 평가한다는 방식을 소개합니다.이후 cross‑entropy difference나 positive–unlabeled learning 등을 이용한 다양한 연구가 나왔던 것 같습니다.그래도 골자는 고품질 데이터(positive)만 학습한 모델의 점수로 문서를 분류하는 것 입니다.저희도 비슷한 아이디어를 적용하여, 먼저 위키백과, 뉴스 등 신뢰할 수 있는 자료만으로 모델을 학습하고,이후 크롤링 데이터에 적용해 퍼플렉서티/확률 분포가 threshold 보다 낮은 문서를 제거하여 noisy 데이터를 줄였습니다.웹 크롤링 데이터에는 한국어 외의 언어가 섞여 있습니다. 사실 한국어 데이터는 비율상 많이 없죠.비록 웹문서 header에 해당하는 부분에 language 정보가 있지만, 데이터를 하나씩 보다 보면 그 정보를 믿기는 어려웠습니다. 그래서 LangID 모듈이 필요하다고 생각했습니다.그 때 당시 외부에서 공개된 언어 식별 모델들이 몇개 있었는데 적용하면, 특이하게도 한국어와 힌디어를 헷갈려하는 등 문제점이 많았습니다.2020년 COLING 논문 Language ID in the Wild에서는 1,629개 언어에 대해 F1 점수는 높았지만 웹 크롤링 데이터에 적용했을 때 저자원 언어의 정밀도가 매우 낮은 수준에 불과하며,도메인 불일치와 클래스 불균형 때문에 오분류가 많다는 점을 레포팅 했습니다.논문은 단어 목록 기반 필터와 Transformer 기반 준지도 학습 모델을 도입하여 데이터셋의 중간값 정밀도를 높였다고 합니다.이런 연구를 참고하여 내부 시스템도 단순 rule‑based 방식에서 벗어나 단어 목록, 언어 모델 출력 등을 조합해 언어를 판별하는 모듈을 만들었습니다.LLM 학습 데이터에서는 중복 문서 제거가 가장 어렵고 비용이 많이 드는 작업입니다. 원칙적으로 모든 문서 쌍을 비교해야 하기에 복잡도가 O(n^2)에 달합니다.한 HackerNoon의 글은 중복 제거가 이러한 이유로 어렵다고 설명하며, 성능을 높이기 위해 blocking key 생성, 해싱 및 Locality Sensitive Hashing(LSH) 등으로 후보 집합을 줄이는 전략을 소개합니다.우리는 한 AWS 엔지니어가 블로그 작성한 Jaccard 유사도와 MinHash를 사용해 near‑duplicate를 찾는 방법을 보았습니다.GPT‑3 논문에서도 사용되었다는 이 방법은 Spark의 MinHashLSH 구현을 이용해 fuzzy dedup을 수행하고, 약 10%의 데이터를 제거했다고 합니다.하지만 실제로 적용해 보니 Spark MinHashLSH가 내부적으로 효율적으로 구현되어 있지 않아 메모리와 시간이 크게 소비되는 문제가 있었고,정확도를 높이려면 hash 테이블 수를 늘려야 하지만, Databricks 사례처럼 테이블을 3개에서 5개로 늘릴 때 시간과 정확도 사이의 trade‑off가 발생한다는 점도 확인했습니다.우리는 결국 데이터 분할과 후보 집합 생성을 반복하며 중복 제거를 수행했지만, 완벽하다고 할 수는 없었습니다.엔비디아 EMNLP 2024 논문 요약2024년 EMNLP에서 엔비디아 연구팀은 Data, Data Everywhere: A Guide for Pretraining Dataset Construction라는 논문을 발표했습니다.LLM 학습 데이터가 중요함에도 불구하고 관련 사례가 제대로 공개되지 않는다는 문제의식에서 출발해,웹 크롤링 기반 코퍼스를 만드는 전 과정을 분석하고 체계화하여 논문으로 publish 했습니다.1. 데이터 큐레이션: 중복 제거와 품질 필터링먼저 정확한 중복 제거와 퍼지(fuzzy) 중복 제거를 수행한다고 소개합니다.정확한 중복 제거에서는 128비트 해시를 사용하여 완전히 같은 문서를 그룹화한 후 하나만 남기는 방식이며,퍼지 중복 제거는 Smith 등(2022)의 LSH 기반 방법을 활용해 유사 문서를 제거하는 방법입니다.이후 품질 필터링 단계에서 CC
8/7/2025
logo
EMNLP24 늦은 후기 2탄: CC 데이터로 LLM 사전학습 데이터셋을 만들어본 경험 및 NVIDIA 논문 리뷰
LLM 시대가 오기 전에 SK텔레콤에서는 일종의 Moon shot 프로젝트를 진행한 적이 있습니다.지금은 LLM(Large Language Model)이라는 이름이 당연하지만 당시에는 GLM(General-Purpose Language Model)이라고 불렸죠.사전학습 모델을 위한 대규모 말뭉치가 필요했지만, 얼마나 필요한지도 몰랐고, 당연하게도 아무런 기반이 없었습니다.그래서 데이터 엔지니어링을 담당했던 저와 동료들은 처음부터 데이터를 모으고 처리하는 파이프라인을 기획하고, PySpark를 이용해 뼈대를 개발했습니다.CCNet: Extracting High Quality Monolingual Datasets from Web Crawl Data 논문을 참고하여 웹 크롤링 데이터에서 한국어 문서를 모으고 선별하는 방식으로 구축했습니다.작년 EMNLP 참가했을 때, 워크숍 세션에서 엔비디아 연구팀은 Pretrain 용 데이터 구축 방법에 대한 노하우를 연구 성과로 발표했습니다.우리 팀에서 과거에 매 과정마다 시행착오를 거치면서 알게 되었던 내용이 체계적으로 정리되어 있어서 크게 반가웠고 많이 공감했습니다.이 글에서는 당시의 좌충우돌 경험을 회상하며 Pretrain Dataset 구축 방법(엔비디아 논문)을 소개해 보려 합니다.CCNet 기반 데이터 파이프라인 구축데이터 구축을 위해 병렬처리가 필요한데, Spark나 Dask도 고민은 했었지만 개발 용이성과 성능 이점 모두를 고려해봤을 때 PySpark가 최선의 선택이라고 생각했습니다.일정 단위로 파이프라인을 스케줄링하기 위해서는 Airflow를 이용했고요. 당시에는 대규모 데이터를 다룰 경험이 많지 않아 매 단계가 도전이었습니다.CCNet 구조를 토대로 기본적인 데이터 흐름을 만들었지만, 실제 적용 과정에서 많은 문제에 부딪혔습니다.그 중 가장 기억에 남는 세가지를 소개합니다.Common Crawl 데이터셋 처럼 웹에서 긁어온 문서는 품질 편차가 매우 큽니다. 그래서 웹 크롤링 데이터를 활용할 때 가장 처음 맞닥뜨리는 문제는 데이터의 질 관리입니다.그래서 CCNet 논문에서도 위키백과 등 고품질 코퍼스만으로 학습한 언어 모델을 사용해 perplexity metric로 문서를 평가한다는 방식을 소개합니다.이후 cross‑entropy difference나 positive–unlabeled learning 등을 이용한 다양한 연구가 나왔던 것 같습니다.그래도 골자는 고품질 데이터(positive)만 학습한 모델의 점수로 문서를 분류하는 것 입니다.저희도 비슷한 아이디어를 적용하여, 먼저 위키백과, 뉴스 등 신뢰할 수 있는 자료만으로 모델을 학습하고,이후 크롤링 데이터에 적용해 퍼플렉서티/확률 분포가 threshold 보다 낮은 문서를 제거하여 noisy 데이터를 줄였습니다.웹 크롤링 데이터에는 한국어 외의 언어가 섞여 있습니다. 사실 한국어 데이터는 비율상 많이 없죠.비록 웹문서 header에 해당하는 부분에 language 정보가 있지만, 데이터를 하나씩 보다 보면 그 정보를 믿기는 어려웠습니다. 그래서 LangID 모듈이 필요하다고 생각했습니다.그 때 당시 외부에서 공개된 언어 식별 모델들이 몇개 있었는데 적용하면, 특이하게도 한국어와 힌디어를 헷갈려하는 등 문제점이 많았습니다.2020년 COLING 논문 Language ID in the Wild에서는 1,629개 언어에 대해 F1 점수는 높았지만 웹 크롤링 데이터에 적용했을 때 저자원 언어의 정밀도가 매우 낮은 수준에 불과하며,도메인 불일치와 클래스 불균형 때문에 오분류가 많다는 점을 레포팅 했습니다.논문은 단어 목록 기반 필터와 Transformer 기반 준지도 학습 모델을 도입하여 데이터셋의 중간값 정밀도를 높였다고 합니다.이런 연구를 참고하여 내부 시스템도 단순 rule‑based 방식에서 벗어나 단어 목록, 언어 모델 출력 등을 조합해 언어를 판별하는 모듈을 만들었습니다.LLM 학습 데이터에서는 중복 문서 제거가 가장 어렵고 비용이 많이 드는 작업입니다. 원칙적으로 모든 문서 쌍을 비교해야 하기에 복잡도가 O(n^2)에 달합니다.한 HackerNoon의 글은 중복 제거가 이러한 이유로 어렵다고 설명하며, 성능을 높이기 위해 blocking key 생성, 해싱 및 Locality Sensitive Hashing(LSH) 등으로 후보 집합을 줄이는 전략을 소개합니다.우리는 한 AWS 엔지니어가 블로그 작성한 Jaccard 유사도와 MinHash를 사용해 near‑duplicate를 찾는 방법을 보았습니다.GPT‑3 논문에서도 사용되었다는 이 방법은 Spark의 MinHashLSH 구현을 이용해 fuzzy dedup을 수행하고, 약 10%의 데이터를 제거했다고 합니다.하지만 실제로 적용해 보니 Spark MinHashLSH가 내부적으로 효율적으로 구현되어 있지 않아 메모리와 시간이 크게 소비되는 문제가 있었고,정확도를 높이려면 hash 테이블 수를 늘려야 하지만, Databricks 사례처럼 테이블을 3개에서 5개로 늘릴 때 시간과 정확도 사이의 trade‑off가 발생한다는 점도 확인했습니다.우리는 결국 데이터 분할과 후보 집합 생성을 반복하며 중복 제거를 수행했지만, 완벽하다고 할 수는 없었습니다.엔비디아 EMNLP 2024 논문 요약2024년 EMNLP에서 엔비디아 연구팀은 Data, Data Everywhere: A Guide for Pretraining Dataset Construction라는 논문을 발표했습니다.LLM 학습 데이터가 중요함에도 불구하고 관련 사례가 제대로 공개되지 않는다는 문제의식에서 출발해,웹 크롤링 기반 코퍼스를 만드는 전 과정을 분석하고 체계화하여 논문으로 publish 했습니다.1. 데이터 큐레이션: 중복 제거와 품질 필터링먼저 정확한 중복 제거와 퍼지(fuzzy) 중복 제거를 수행한다고 소개합니다.정확한 중복 제거에서는 128비트 해시를 사용하여 완전히 같은 문서를 그룹화한 후 하나만 남기는 방식이며,퍼지 중복 제거는 Smith 등(2022)의 LSH 기반 방법을 활용해 유사 문서를 제거하는 방법입니다.이후 품질 필터링 단계에서 CC
2025.08.07
emoji
좋아요
emoji
별로에요
logo
FE News 25년 8월
주요소식25년 8월 소식에서는 다음과 같은 유용한 정보들을 만나보실 수 있습니다.링크 & 읽을거리JSNation 2025: GitNation이 주최하는 JavaScript 컨퍼런스로, JavaScript관련된 다양한 주제의 세션들을 볼 수 있다.Tech-Verse 2025: 라인야후애서 주최한 최신 기술 트렌드와 개발 방법론을 공유하는 개발자 중심의 컨퍼런스다.AI Design Guide: AI와 디자인의 결합으로 가능한 새로운 워크플로와 방법론을 소개하는 리소스 컬렉션으로, 프롬프트 작성법부터 디자인 자동화까지 실용적인 팁을 제공한다.The New Code Sean Grove, OpenAI: AI 시대에 가장 가치 있는 기술은 코드 작성이 아닌 의도를 정확히 전달하는 것이라는 관점을 제시한다.Can AI Replace Web Developers?: "AI가 웹 개발자를 대체할 수 있을까?"라는 주제를 다룬 글이다.Ultimate Guide to Vibe Coding: AI 시대의 새로운 코딩 접근 방식인 'Vibe Coding'에 대한 가이드를 제공한다.튜토리얼Claude Code 마스터하기: 카카오의 황민호님이 작성한 Claude를 개발 환경에서 효과적으로 활용하는 방법에 대한 포괄적인 가이드를 제공한다.Reactive State Manager With Proxies: JavaScript의 Proxy 객체를 활용해 Reactive State Manager를 직접 구현하는 과정을 소개한다.jsdate.wtf: JavaScript의 Date 객체가 얼마나 혼란스럽고 예측하기 어려운지 보여주는 인터랙티브 웹사이트이다.A Friendly Introduction to SVG: SVG의 기본 개념부터 실용적인 활용법까지 친근하게 소개하는 포괄적인 가이드를 제공한다.코드와 도구n8n - Flexible AI workflow automation for technical teams: 프런트엔드 개발자들이 API 통합과 워크플로우 자동화를 위해 주목해야 할 오픈소스 워크플로우 자동화 플랫폼이다.Code Canvas App: VSCode 확장 프로그램으로, 코드 시각화와 구조화에 혁신적인 접근 방식을 제공한다.Awesome Context Engineering: AI 개발에 있어 중요성이 날로 커지고 있는 '컨텍스트 엔지니어링'에 관한 포괄적인 리소스 모음을 제공한다.Bruno - API Client: Postman과 Insomnia의 대안으로 등장한 API 탐색과 테스트를 위한 오픈소스 IDE이다.>> FE News 25년 8월 소식 보러가기 FE News란? 네이버 FE 엔지니어들이 엄선한 양질의 FE 및 주요한 기술 소식들을 큐레이션해 공유하는 것을 목표로 하며, 이를 통해 국내 개발자들에게 지식 공유에 대한 가치 인식과 성장에 도움을 주고자 하는 기술소식 공유 프로젝트 입니다. 매월 첫째 주 수요일, 월 1회 발행 되고 있으니 많은 관심 부탁드립니다. 구독하기
javascript
8/7/2025
logo
FE News 25년 8월
주요소식25년 8월 소식에서는 다음과 같은 유용한 정보들을 만나보실 수 있습니다.링크 & 읽을거리JSNation 2025: GitNation이 주최하는 JavaScript 컨퍼런스로, JavaScript관련된 다양한 주제의 세션들을 볼 수 있다.Tech-Verse 2025: 라인야후애서 주최한 최신 기술 트렌드와 개발 방법론을 공유하는 개발자 중심의 컨퍼런스다.AI Design Guide: AI와 디자인의 결합으로 가능한 새로운 워크플로와 방법론을 소개하는 리소스 컬렉션으로, 프롬프트 작성법부터 디자인 자동화까지 실용적인 팁을 제공한다.The New Code Sean Grove, OpenAI: AI 시대에 가장 가치 있는 기술은 코드 작성이 아닌 의도를 정확히 전달하는 것이라는 관점을 제시한다.Can AI Replace Web Developers?: "AI가 웹 개발자를 대체할 수 있을까?"라는 주제를 다룬 글이다.Ultimate Guide to Vibe Coding: AI 시대의 새로운 코딩 접근 방식인 'Vibe Coding'에 대한 가이드를 제공한다.튜토리얼Claude Code 마스터하기: 카카오의 황민호님이 작성한 Claude를 개발 환경에서 효과적으로 활용하는 방법에 대한 포괄적인 가이드를 제공한다.Reactive State Manager With Proxies: JavaScript의 Proxy 객체를 활용해 Reactive State Manager를 직접 구현하는 과정을 소개한다.jsdate.wtf: JavaScript의 Date 객체가 얼마나 혼란스럽고 예측하기 어려운지 보여주는 인터랙티브 웹사이트이다.A Friendly Introduction to SVG: SVG의 기본 개념부터 실용적인 활용법까지 친근하게 소개하는 포괄적인 가이드를 제공한다.코드와 도구n8n - Flexible AI workflow automation for technical teams: 프런트엔드 개발자들이 API 통합과 워크플로우 자동화를 위해 주목해야 할 오픈소스 워크플로우 자동화 플랫폼이다.Code Canvas App: VSCode 확장 프로그램으로, 코드 시각화와 구조화에 혁신적인 접근 방식을 제공한다.Awesome Context Engineering: AI 개발에 있어 중요성이 날로 커지고 있는 '컨텍스트 엔지니어링'에 관한 포괄적인 리소스 모음을 제공한다.Bruno - API Client: Postman과 Insomnia의 대안으로 등장한 API 탐색과 테스트를 위한 오픈소스 IDE이다.>> FE News 25년 8월 소식 보러가기 FE News란? 네이버 FE 엔지니어들이 엄선한 양질의 FE 및 주요한 기술 소식들을 큐레이션해 공유하는 것을 목표로 하며, 이를 통해 국내 개발자들에게 지식 공유에 대한 가치 인식과 성장에 도움을 주고자 하는 기술소식 공유 프로젝트 입니다. 매월 첫째 주 수요일, 월 1회 발행 되고 있으니 많은 관심 부탁드립니다. 구독하기
2025.08.07
javascript
emoji
좋아요
emoji
별로에요
logo
How Was the New QANDA Agent Built?
The New QANDA Agent Is HereDesigning a smarter, more powerful AgentHello, I m Dave, a Backend Engineer at QANDA.Some of you may have been surprised by the recent changes on QANDA. In response to the era of Generative AI, we ve gone beyond simple feature upgrades to conduct a full redesign   ranging from the main screen of the app to the overall user journey.In this blog post, we ll walk you through:The new direction of QANDA s AI product in the Gen AI eraThe technical approaches our backend team took to bring that vision to lifeWe hope this provides practical insights for anyone interested in the design and architecture of AI-powered services.Why and How Has QANDA Changed?The previous version of the QANDA app was best known for its core feature: allowing students to take a photo of a question they didn t understand and receive an AI-generated solution.However, this feature was heavily focused on a specific use case   providing solutions when a student encounters a difficult problem.As a result, it had limitations when it came to supporting a broader range of learning goals, such as concept explanations, recommending similar problems, or generating quizzes. A Newly Defined DirectionSo, we set a new goal: Understand the user s learning intent in real time, and leverage LLMs to deliver optimal UI/UX accordingly. With this new direction, QANDA has been transformed from a simple problem-solving assistant into a proactive AI learning assistant capable of supporting a wide range of educational needs.A Completely New Engineering ParadigmAt its core, the new system we built was what s commonly referred to as an Agent. Instead of simply reacting based on predefined logic, we needed a structure that could understand a wide range of user requests and intelligently select the appropriate tool to solve the problem.Initially, we followed a typical product development process: listing out user needs, defining features for each, and then implementing them in the UI. For example: I don t know how to solve this problem. Provide step-by-step solution I solved it myself   can you check if it s correct? Review and give feedback on handwritten answers I don t understand the concept. Explain the concept with examples I want to practice similar questions. Search and recommend similar problems I think I m starting to get it. Generate a quiz to check understandingIn a traditional setup, each of these requests would have resulted in a separate API and dedicated UI components to match.However, we chose a different approach: a unified Agent that drives the interaction with students through a single chat-based interface.In other words, our goal was to build a structure capable of handling diverse user needs within a single conversational flow. To make this possible, there were three key technical challenges we had to carefully consider: 1. Defining Chat Interface Components The system needed to dynamically combine different blocks based on user requests. 2. Agent Intent Analys
reactjs
8/7/2025
logo
How Was the New QANDA Agent Built?
The New QANDA Agent Is HereDesigning a smarter, more powerful AgentHello, I m Dave, a Backend Engineer at QANDA.Some of you may have been surprised by the recent changes on QANDA. In response to the era of Generative AI, we ve gone beyond simple feature upgrades to conduct a full redesign   ranging from the main screen of the app to the overall user journey.In this blog post, we ll walk you through:The new direction of QANDA s AI product in the Gen AI eraThe technical approaches our backend team took to bring that vision to lifeWe hope this provides practical insights for anyone interested in the design and architecture of AI-powered services.Why and How Has QANDA Changed?The previous version of the QANDA app was best known for its core feature: allowing students to take a photo of a question they didn t understand and receive an AI-generated solution.However, this feature was heavily focused on a specific use case   providing solutions when a student encounters a difficult problem.As a result, it had limitations when it came to supporting a broader range of learning goals, such as concept explanations, recommending similar problems, or generating quizzes. A Newly Defined DirectionSo, we set a new goal: Understand the user s learning intent in real time, and leverage LLMs to deliver optimal UI/UX accordingly. With this new direction, QANDA has been transformed from a simple problem-solving assistant into a proactive AI learning assistant capable of supporting a wide range of educational needs.A Completely New Engineering ParadigmAt its core, the new system we built was what s commonly referred to as an Agent. Instead of simply reacting based on predefined logic, we needed a structure that could understand a wide range of user requests and intelligently select the appropriate tool to solve the problem.Initially, we followed a typical product development process: listing out user needs, defining features for each, and then implementing them in the UI. For example: I don t know how to solve this problem. Provide step-by-step solution I solved it myself   can you check if it s correct? Review and give feedback on handwritten answers I don t understand the concept. Explain the concept with examples I want to practice similar questions. Search and recommend similar problems I think I m starting to get it. Generate a quiz to check understandingIn a traditional setup, each of these requests would have resulted in a separate API and dedicated UI components to match.However, we chose a different approach: a unified Agent that drives the interaction with students through a single chat-based interface.In other words, our goal was to build a structure capable of handling diverse user needs within a single conversational flow. To make this possible, there were three key technical challenges we had to carefully consider: 1. Defining Chat Interface Components The system needed to dynamically combine different blocks based on user requests. 2. Agent Intent Analys
2025.08.07
reactjs
emoji
좋아요
emoji
별로에요
logo
Nginx 설정 통합과 Loki 연동으로 설계한 유연한 멀티사이트 아키텍처
안녕하세요. LINE NEXT DevOps 팀에서 일하고 있는 이동원이라고 합니다. 현재 쿠버네티스 기반의 인프라 운영과 유지 보수, CI/CD 구축, 로그 수집 및 모니터링 시스템 구성, 장애 대응 등 인프라의 전반적인 업 무를 담당하고 있습니다. 이번 글에서는 LINE NEXT의 공통 인프라 중 Nginx 레이어 통합 및 활용 사례를 소개하려고 합니다.저는 다수의 서비스를 론칭해 온 노하우에 기반해 2022년 DOSI 서비스를 시작으로 2025년에는 현재까지 Dapp Portal과 Molts, Next Market 등 다수의 서비스에 빠르게 웹 서버 인프라를 제공해 왔습니다.인프라를 제공할 때는 애플리케이션 환경을 구성하기 전에 반드시 웹 서버(Nginx) 레이어를 반복적으로 구성해야 합니다. 그런데 기존에는 이 과정에 꽤나 많은 리드 타임이 필요하다는 문제가 있었습니다. LINE NEXT의 인프라는 초기 구성 방식의 특성상 새로운 서버와 로드밸런서를 발급받는 데에만 최소 일주일이 소요됐고, 이후 Nginx 설치 및 설정과 배포, 앞단의 로드 밸런서 세팅까지 포함하면 최소 2주 정도의 리드 타임이 필요했습니다.또한 현재 LINE NEXT는 글로벌 대상의 다양한 브랜드 및 서비스 도메인을 운영하고 있으며, 각각의 서브 도메인까지 포함하면 약 100여 개에 달하는 도메인을 관리하고 있는데요. 이처럼 빠르게 확장되는 서비스 환경에서 매번 수동으로 웹 서버를 구성하는 방식은 점점 한계에 다다랐습니다.저는 이런 문제들을 해결하기 위해 3단계에 걸쳐 웹 서버 인프라의 아키텍처를 개선했습니다. 그 과정에서 기존 단일 구성에서 벗어나 공통 기능을 하나로 통합하면서도 새로운 서비스에 영향을 주지 않도록 신경 써야 했으며, 아래와 같은 기능 요소들도 고려해야 했습니다.이번 글에서는 수동으로 웹 서버를 구성해야 했던 기존 구조를 개선하기 위해 'Nginx 설정을 통합하고 멀티사이트 구조를 도입한 뒤 안정적이고 확장 가능한 로그 수집 체계를 구축한 과정'을 차례대로 살펴보겠습니다. 또한 이렇게 완성해 나간 구조에서 위에서 나열한 각 기능 요소를 어떻게 구현 및 구성했는지 하나씩 소개하겠습니다.LINE NEXT에서는 점점 잦아지는 서비스 론칭 요구에 대응하기 위해 웹 서버(Nginx) 구성을 중심으로 배포 구조와 아키텍처를 지속적으로 개선해 왔습니다. 특히 설정 과정에서의 반복 작업과 리드 타임을 줄이기 위해 기존의 파편화된 구조를 총 3단계에 걸쳐 점진적으로 중앙 집중화된 통합 구조로 전환했는데요. 각 구조의 특징과 다음 구조로 전환한 이유를 살펴보겠습니다.초기에는 사내에서 제공하는 배포 도구인 PMC(Project Management Console)를 이용해 각 서비스의 Nginx 설정을 관리했습니다.PMC는 설정 파일과 Nginx 대상 서버를 정의한 후 rsync를 이용해 타깃 서버에 설정을 배포하는 방식으로 작동합니다. 이 구조에서는 Nginx 대상 서버에 SSH를 통해 직접 접근해야 하기 때문에 22번 포트(SSH) 를 열어두어야 하는데요. 사내 시스템이긴 하지만 보안망으로 22번 포트를 허용해야 하기 때문에 보안 관점에서 리스크가 있었습니다.또한 초기 구성에는 다음과 같은 한계도 있었습니다.• 서비스별로 별도로 Nginx 서버와 로드밸런서를 운영해야 했기에 구조가 파편화됐습니다.• 새로운 서비스를 론칭할 때마다 서버를 새로 요청한 뒤 Nginx를 설치하고 PMC 설정을 구성하는 작업을 반복해야 했습니다.• Nginx 설정을 개별 리포지터리에서 운영했기에 각 팀별로 설정이 달라지면서 유지 보수 측면에서 어려움이 발생했습니다.초기에는 서비스별로 독립적으로 설정할 수 있다는 장점으로 작용했던 구조의 특성이 서비스가 많아지면서 유지 보수와 확장성 측면에서 부담이 커진다는 단점으로 바뀌었고, 이에 Ingress Nginx 기반의 인프라 구조로 변경하기로 결정했습니다.LINE NEXT에서는 애플리케이션의 기본 인프라로 쿠버네티스를 활용하고 있었습니다. 저희는 Nginx도 쿠버네티스로 운영하기 위해 Ingress Nginx를 도입했고, 이에 따라 도메인 설정 및 Nginx 설정 프로세스에 큰 변화가 발생했습니다.Ingress Nginx 구조의 핵심은 다음과 같습니다.• 로드 밸런서를 고정으로 두고(프로덕션 기준 4개의 하드웨어 로드밸런서 사용), Ingress Nginx의 NodePort 연결을 통해 트래픽을 분산• 헬름 차트의 values.yaml 파일을 이용해 도메인, 인증서, 타깃 서비스(애플리케이션) 이름, 타깃 포트 등을 정의하면 바로 쿠버네티스의 Ingress 리소스를 통해 연결할 수 있도록 구성• 별도의 로드 밸런서나 Nginx 서버를 구성할 필요 없이 헬름 차트 설정만으로 도메인과 웹 서버 설정 가능이 구조는 PMC 기반의 인프라 구조 대비 다음과 같은 장점이 있습니다.• 서비스 론칭 속도가 비약적으로 향상되고, 반복 수작업이 줄어들었습니다.• 인증서 적용과 도메인 설정 등이 설정 중심의 방식으로 추상화됐습니다.다만 이 구조에는 한 가지 중요한 단점이 존재했는데요. 외부 요청이 로드밸런서(프락시 모드)에서 Ingress Nginx(NodePort)를 통해 전달될 때 클라이언트의 실제 IP(원격 주소)를 확인하기 어렵다는 것입니다. 또한 Nginx의 세부 기능(예: GeoIP 모듈, 정교한 위치 설정 등)을 활용하기 어렵다는 제약이 있었습니다.위와 같은 Ingress Nginx 구조의 한계를 극복하고 공통 설정과 고급 기능을 새로운 서비스에 유연하게 반영할 수 있도록 최종적으로 네이티브 Nginx를 직접 운영하는 구조로 전환했습니다. 다만 단순히 서버를 직접 운영하는 방식이 아니라, Ingress Nginx처럼 설정 중심의 방식은 유지하면서도 Nginx의 기본 기능을 충분히 활용할 수 있는 하이브리드 구조로 설계했습니다.주요 변화 요소는 다음과 같습니 다.• GeoIP 모듈, Loki 기반 로그 수집, 인증서 관리, 점검(maintenance) 모드 설정, 유연한 위치 설정 등 다양한 기능을 공통으로 탑재할 수 있게 구성• Nginx 설정만 추가하면 기존 공통 설정과 함께 바로
ansible
helm
kubernetes
8/7/2025
logo
Nginx 설정 통합과 Loki 연동으로 설계한 유연한 멀티사이트 아키텍처
안녕하세요. LINE NEXT DevOps 팀에서 일하고 있는 이동원이라고 합니다. 현재 쿠버네티스 기반의 인프라 운영과 유지 보수, CI/CD 구축, 로그 수집 및 모니터링 시스템 구성, 장애 대응 등 인프라의 전반적인 업 무를 담당하고 있습니다. 이번 글에서는 LINE NEXT의 공통 인프라 중 Nginx 레이어 통합 및 활용 사례를 소개하려고 합니다.저는 다수의 서비스를 론칭해 온 노하우에 기반해 2022년 DOSI 서비스를 시작으로 2025년에는 현재까지 Dapp Portal과 Molts, Next Market 등 다수의 서비스에 빠르게 웹 서버 인프라를 제공해 왔습니다.인프라를 제공할 때는 애플리케이션 환경을 구성하기 전에 반드시 웹 서버(Nginx) 레이어를 반복적으로 구성해야 합니다. 그런데 기존에는 이 과정에 꽤나 많은 리드 타임이 필요하다는 문제가 있었습니다. LINE NEXT의 인프라는 초기 구성 방식의 특성상 새로운 서버와 로드밸런서를 발급받는 데에만 최소 일주일이 소요됐고, 이후 Nginx 설치 및 설정과 배포, 앞단의 로드 밸런서 세팅까지 포함하면 최소 2주 정도의 리드 타임이 필요했습니다.또한 현재 LINE NEXT는 글로벌 대상의 다양한 브랜드 및 서비스 도메인을 운영하고 있으며, 각각의 서브 도메인까지 포함하면 약 100여 개에 달하는 도메인을 관리하고 있는데요. 이처럼 빠르게 확장되는 서비스 환경에서 매번 수동으로 웹 서버를 구성하는 방식은 점점 한계에 다다랐습니다.저는 이런 문제들을 해결하기 위해 3단계에 걸쳐 웹 서버 인프라의 아키텍처를 개선했습니다. 그 과정에서 기존 단일 구성에서 벗어나 공통 기능을 하나로 통합하면서도 새로운 서비스에 영향을 주지 않도록 신경 써야 했으며, 아래와 같은 기능 요소들도 고려해야 했습니다.이번 글에서는 수동으로 웹 서버를 구성해야 했던 기존 구조를 개선하기 위해 'Nginx 설정을 통합하고 멀티사이트 구조를 도입한 뒤 안정적이고 확장 가능한 로그 수집 체계를 구축한 과정'을 차례대로 살펴보겠습니다. 또한 이렇게 완성해 나간 구조에서 위에서 나열한 각 기능 요소를 어떻게 구현 및 구성했는지 하나씩 소개하겠습니다.LINE NEXT에서는 점점 잦아지는 서비스 론칭 요구에 대응하기 위해 웹 서버(Nginx) 구성을 중심으로 배포 구조와 아키텍처를 지속적으로 개선해 왔습니다. 특히 설정 과정에서의 반복 작업과 리드 타임을 줄이기 위해 기존의 파편화된 구조를 총 3단계에 걸쳐 점진적으로 중앙 집중화된 통합 구조로 전환했는데요. 각 구조의 특징과 다음 구조로 전환한 이유를 살펴보겠습니다.초기에는 사내에서 제공하는 배포 도구인 PMC(Project Management Console)를 이용해 각 서비스의 Nginx 설정을 관리했습니다.PMC는 설정 파일과 Nginx 대상 서버를 정의한 후 rsync를 이용해 타깃 서버에 설정을 배포하는 방식으로 작동합니다. 이 구조에서는 Nginx 대상 서버에 SSH를 통해 직접 접근해야 하기 때문에 22번 포트(SSH) 를 열어두어야 하는데요. 사내 시스템이긴 하지만 보안망으로 22번 포트를 허용해야 하기 때문에 보안 관점에서 리스크가 있었습니다.또한 초기 구성에는 다음과 같은 한계도 있었습니다.• 서비스별로 별도로 Nginx 서버와 로드밸런서를 운영해야 했기에 구조가 파편화됐습니다.• 새로운 서비스를 론칭할 때마다 서버를 새로 요청한 뒤 Nginx를 설치하고 PMC 설정을 구성하는 작업을 반복해야 했습니다.• Nginx 설정을 개별 리포지터리에서 운영했기에 각 팀별로 설정이 달라지면서 유지 보수 측면에서 어려움이 발생했습니다.초기에는 서비스별로 독립적으로 설정할 수 있다는 장점으로 작용했던 구조의 특성이 서비스가 많아지면서 유지 보수와 확장성 측면에서 부담이 커진다는 단점으로 바뀌었고, 이에 Ingress Nginx 기반의 인프라 구조로 변경하기로 결정했습니다.LINE NEXT에서는 애플리케이션의 기본 인프라로 쿠버네티스를 활용하고 있었습니다. 저희는 Nginx도 쿠버네티스로 운영하기 위해 Ingress Nginx를 도입했고, 이에 따라 도메인 설정 및 Nginx 설정 프로세스에 큰 변화가 발생했습니다.Ingress Nginx 구조의 핵심은 다음과 같습니다.• 로드 밸런서를 고정으로 두고(프로덕션 기준 4개의 하드웨어 로드밸런서 사용), Ingress Nginx의 NodePort 연결을 통해 트래픽을 분산• 헬름 차트의 values.yaml 파일을 이용해 도메인, 인증서, 타깃 서비스(애플리케이션) 이름, 타깃 포트 등을 정의하면 바로 쿠버네티스의 Ingress 리소스를 통해 연결할 수 있도록 구성• 별도의 로드 밸런서나 Nginx 서버를 구성할 필요 없이 헬름 차트 설정만으로 도메인과 웹 서버 설정 가능이 구조는 PMC 기반의 인프라 구조 대비 다음과 같은 장점이 있습니다.• 서비스 론칭 속도가 비약적으로 향상되고, 반복 수작업이 줄어들었습니다.• 인증서 적용과 도메인 설정 등이 설정 중심의 방식으로 추상화됐습니다.다만 이 구조에는 한 가지 중요한 단점이 존재했는데요. 외부 요청이 로드밸런서(프락시 모드)에서 Ingress Nginx(NodePort)를 통해 전달될 때 클라이언트의 실제 IP(원격 주소)를 확인하기 어렵다는 것입니다. 또한 Nginx의 세부 기능(예: GeoIP 모듈, 정교한 위치 설정 등)을 활용하기 어렵다는 제약이 있었습니다.위와 같은 Ingress Nginx 구조의 한계를 극복하고 공통 설정과 고급 기능을 새로운 서비스에 유연하게 반영할 수 있도록 최종적으로 네이티브 Nginx를 직접 운영하는 구조로 전환했습니다. 다만 단순히 서버를 직접 운영하는 방식이 아니라, Ingress Nginx처럼 설정 중심의 방식은 유지하면서도 Nginx의 기본 기능을 충분히 활용할 수 있는 하이브리드 구조로 설계했습니다.주요 변화 요소는 다음과 같습니 다.• GeoIP 모듈, Loki 기반 로그 수집, 인증서 관리, 점검(maintenance) 모드 설정, 유연한 위치 설정 등 다양한 기능을 공통으로 탑재할 수 있게 구성• Nginx 설정만 추가하면 기존 공통 설정과 함께 바로
2025.08.07
ansible
helm
kubernetes
emoji
좋아요
emoji
별로에요
logo
자네, 해커가 되지 않겠나? Hack Day 2025에 다녀왔습니다!
안녕하세요. LINE Plus 소속 Redis 팀의 김정훈입니다. 저는 지난 7월 2일부터 4일까지 사흘간 진행된 사내 해커톤 'Hack Day 2025'에 참가했습니다. Hack Day 2025는 작년과 마찬가지로 도쿄 기오이초에 위치한 LY Corporation 오피스에서 개최됐습니다(작년 후기는 다른 분들께서 남겨 주신 Tech Week 2024, 사내 해커톤 Hack Day에 참여했습니다!를 참고해 주세요!).사실 저는 작년에도 Hack Day에 참가했는데요. 무려 2년간의 경험을 토대로 여러분들과 함께 사내 해커톤 Hack Day의 즐거움을 나눠 보고자 합니다.Hack Day는 과거 합병 전 Yahoo Japan Corporation에서 매년 개최해 온 사내 해커톤 행사입니다. 2007년에 시작해서 합병으로 LY Corporation(이하 LY)이 된 이후에도 꾸준히 이어져 올해로 무려 19회차를 맞이했습니다.Hack Day는 '9 to 9'으로 이틀간 총 24시간에 걸쳐 진행된 후 다음 날 시상식으로 마무리되는 행사입니다. 참고로 이 기간에는 LY의 또 다른 연례 행사인 테크 컨퍼런스, Tech Verse도 함께 개최됩니다. Tech Verse의 생동감 넘치는 후기도 곧 이 블로그에 올라올 예정이니, 많은 관심 부탁드립니다!사내 해커톤이라고 해서 꼭 직무와의 연관성을 찾을 필요는 없습니다. 완전히 자유 주제로 진행되기 때문에 각자가 원하는 프로덕트를 개발하면 됩니다. 또한 '개발'이라고 해서 개발자만의 행사인 것도 아닙니다. 디자이너와 기획자에서 HR까지, 사내 구성원이라면 모두가 각자의 장점을 살려 Hack Day 참가자(이하 해커)가 될 수 있습니다.주제뿐 아니라 팀도 자유롭게 꾸릴 수 있습니다. 같은 법인의 사람들끼리 모여 'General Team'을 구성할 수도 있고, 글로벌 회사답게 다른 나라의 법인에서 온 분들과 함께 'Global Mixed Team'으로 참가하는 것도 가능합니다. 저는 작년에는 구 Yahoo Japan Corporation의 Redis 팀원 분들과 함께 Global Mixed Team으로 참가했는데요. 올해에는 최근 협업을 시작한 한국 분들과 함께 팀을 꾸려 General Team으로 참가했습니다. 팀원들과 밀도 높게 붙게 되는 해커톤이 앞으로의 협업에 큰 이점으로 다가온다고 생각해 2년 연속 이런 형태로 팀을 꾸려봤네요.만약 주위 동료가 해커톤에 관심 없거나, 해외 동료들과 함께 해커톤에 참여하고 싶지만 아는 사람이 없다고 해도 전혀 걱정할 필요는 없습니다. 사내 Developer Relations 팀(이하 DevRel 팀)에서 팀 매칭을 위한 밋업을 주관해 주시기 때문입니다. 제 주위에는 이 밋업을 적극적으로 이용해 참가하시는 분들도 많이 계셨습니다. 팀 매칭 밋업은 여러 나라와 지역의 멤버들이 모여야 하고, 현재 Hybrid Work 2.0 근무 제도를 채택하고 있는 한국 법인의 특성을 고려해 Zoom과 Miro 등 온라인 협업 툴을 적극적으로 활용해 진행됐습니다.각 팀은 팀을 구성하고 사전 심사를 거치는 과정에서부터 대략적인 아이디어가 도출돼 있는 상황이었고, 참가자 발표 시점을 기점으로 기획 구체화 및 기술 검토, 환경 준비 등 여러 가지를 준비하기 시작했습니다. 아무래도 모두가 자유 주제로 자신의 꿈을 펼치는 장이다 보니 본연의 업무와는 동떨어진 일을 하게 되는 경우가 많이 있었을 것 같습니다(네, 맞습니다, 저희 팀 이야기입니다). 준비는 행사 본연의 취지인 '24시간 안에 만들어 낸다'를 해치지 않는 선에서 간단히 아이디어를 검증하는 수준으로만 진행했고, 본격적인 개발은 Hack Day를 위해 남겨뒀습니다.저희 팀은 Hybrid Work 2.0을 채택하고 있는 회사의 특성상 원격으로 일하는 경우가 많았는데요. 기획과 검증 과정에서 단기간에 빠르게 의사를 결정하기 위해 두세 번 정도 오프라인에서 회의를 진행했습니다. 또한 저희 법인의 경우 출장 시 'Travel Day'라는 이름으로 이동 시간과 출장지에서 업무에 집중할 수 있도록 준비할 수 있는 시간을 마련해 주는데요. 이 시간도 다 같이 모여 마지막으로 점검하는 시간으로 활용했습니다.기술 검토를 마치고 기획을 구체화하는 작업까지 완료하고 나니 이제 정말 Hack Day가 시작된다는 것을 피부로 느낄 수 있었습니다(물론 기획은 개발 도중 시시각각 바뀌었지만요 😂).대망의 Hack Day 1일 차, 설레는 마음으로 오피스를 방문해 리셉션에서 출장 기간 동안 사용할 방문증을 배부 받았습니다. 저는 이번 출장에 Hack Day의 현장을 취재하고 담아오는 'LINE DEV 리포터즈'의 역할도 담당하고 있었기에 그 소임을 다하기 위해 다소 일찍 오피스에 도착한 편이었는데요. 벌써부터 몇몇 팀은 회의를 시작하고 있었습니다.아무래도 인원수가 많다 보니 행사장은 오피스 1개 층을 통째로 사용하고 있었고, 팀 간 공 간이 분리돼 있었습니다. 돌이켜 생각해 보니 Hack Day 내내 쾌적하다는 느낌을 받았는데요. 작년 대비 더 넓은 공간을 빌려 인구 밀집도를 낮춰서 개발에 몰두할 수 있는 환경이 제공됐기 때문이라고 생각합니다.매년 꾸준히 개최되는 행사인 만큼 전통이 하나 있습니다. 바로 모든 해커들이 모여서 개회 선언을 하는 것입니다. 1일 차 오전 9시, 모두가 한자리에 모여 크게 "Hack Day!"라고 외치며 이제 진짜로 해커톤이 시작됐음을 알렸습니다.개회식이 끝나자마자 모든 해커들은 지체 없이 자리에 앉아 개발에 착수했습니다. 도합 24시간이라는 시간은 길다면 길고 짧다면 짧은 시간으로, 한 끗 차이로도 완성도가 달라질 수 있기 때문에 모두가 'Perfect the Details'를 향해 달리기 시작했습니다. 행사장에 준비된 모니터와 곳곳에 배치된 화이트보드 등 주위의 모든 자원을 활용하는 모습이 인상 깊었습니다.끊이지 않는 개발, 그리고 끊이지 않는 간식저는 입사 이전에도 이곳저곳의 해커톤 행사에 다녀본 경험이 있는데요. 부끄럽지만 제가 생각하는 해커톤의 꽃은 바로 '끊이지 않는 간식'입니다. 사소해 보일 수 있지만 단기간에 폭발적으로 머리를 쓰는 만큼 당분과 카페인 섭취는
8/7/2025
logo
자네, 해커가 되지 않겠나? Hack Day 2025에 다녀왔습니다!
안녕하세요. LINE Plus 소속 Redis 팀의 김정훈입니다. 저는 지난 7월 2일부터 4일까지 사흘간 진행된 사내 해커톤 'Hack Day 2025'에 참가했습니다. Hack Day 2025는 작년과 마찬가지로 도쿄 기오이초에 위치한 LY Corporation 오피스에서 개최됐습니다(작년 후기는 다른 분들께서 남겨 주신 Tech Week 2024, 사내 해커톤 Hack Day에 참여했습니다!를 참고해 주세요!).사실 저는 작년에도 Hack Day에 참가했는데요. 무려 2년간의 경험을 토대로 여러분들과 함께 사내 해커톤 Hack Day의 즐거움을 나눠 보고자 합니다.Hack Day는 과거 합병 전 Yahoo Japan Corporation에서 매년 개최해 온 사내 해커톤 행사입니다. 2007년에 시작해서 합병으로 LY Corporation(이하 LY)이 된 이후에도 꾸준히 이어져 올해로 무려 19회차를 맞이했습니다.Hack Day는 '9 to 9'으로 이틀간 총 24시간에 걸쳐 진행된 후 다음 날 시상식으로 마무리되는 행사입니다. 참고로 이 기간에는 LY의 또 다른 연례 행사인 테크 컨퍼런스, Tech Verse도 함께 개최됩니다. Tech Verse의 생동감 넘치는 후기도 곧 이 블로그에 올라올 예정이니, 많은 관심 부탁드립니다!사내 해커톤이라고 해서 꼭 직무와의 연관성을 찾을 필요는 없습니다. 완전히 자유 주제로 진행되기 때문에 각자가 원하는 프로덕트를 개발하면 됩니다. 또한 '개발'이라고 해서 개발자만의 행사인 것도 아닙니다. 디자이너와 기획자에서 HR까지, 사내 구성원이라면 모두가 각자의 장점을 살려 Hack Day 참가자(이하 해커)가 될 수 있습니다.주제뿐 아니라 팀도 자유롭게 꾸릴 수 있습니다. 같은 법인의 사람들끼리 모여 'General Team'을 구성할 수도 있고, 글로벌 회사답게 다른 나라의 법인에서 온 분들과 함께 'Global Mixed Team'으로 참가하는 것도 가능합니다. 저는 작년에는 구 Yahoo Japan Corporation의 Redis 팀원 분들과 함께 Global Mixed Team으로 참가했는데요. 올해에는 최근 협업을 시작한 한국 분들과 함께 팀을 꾸려 General Team으로 참가했습니다. 팀원들과 밀도 높게 붙게 되는 해커톤이 앞으로의 협업에 큰 이점으로 다가온다고 생각해 2년 연속 이런 형태로 팀을 꾸려봤네요.만약 주위 동료가 해커톤에 관심 없거나, 해외 동료들과 함께 해커톤에 참여하고 싶지만 아는 사람이 없다고 해도 전혀 걱정할 필요는 없습니다. 사내 Developer Relations 팀(이하 DevRel 팀)에서 팀 매칭을 위한 밋업을 주관해 주시기 때문입니다. 제 주위에는 이 밋업을 적극적으로 이용해 참가하시는 분들도 많이 계셨습니다. 팀 매칭 밋업은 여러 나라와 지역의 멤버들이 모여야 하고, 현재 Hybrid Work 2.0 근무 제도를 채택하고 있는 한국 법인의 특성을 고려해 Zoom과 Miro 등 온라인 협업 툴을 적극적으로 활용해 진행됐습니다.각 팀은 팀을 구성하고 사전 심사를 거치는 과정에서부터 대략적인 아이디어가 도출돼 있는 상황이었고, 참가자 발표 시점을 기점으로 기획 구체화 및 기술 검토, 환경 준비 등 여러 가지를 준비하기 시작했습니다. 아무래도 모두가 자유 주제로 자신의 꿈을 펼치는 장이다 보니 본연의 업무와는 동떨어진 일을 하게 되는 경우가 많이 있었을 것 같습니다(네, 맞습니다, 저희 팀 이야기입니다). 준비는 행사 본연의 취지인 '24시간 안에 만들어 낸다'를 해치지 않는 선에서 간단히 아이디어를 검증하는 수준으로만 진행했고, 본격적인 개발은 Hack Day를 위해 남겨뒀습니다.저희 팀은 Hybrid Work 2.0을 채택하고 있는 회사의 특성상 원격으로 일하는 경우가 많았는데요. 기획과 검증 과정에서 단기간에 빠르게 의사를 결정하기 위해 두세 번 정도 오프라인에서 회의를 진행했습니다. 또한 저희 법인의 경우 출장 시 'Travel Day'라는 이름으로 이동 시간과 출장지에서 업무에 집중할 수 있도록 준비할 수 있는 시간을 마련해 주는데요. 이 시간도 다 같이 모여 마지막으로 점검하는 시간으로 활용했습니다.기술 검토를 마치고 기획을 구체화하는 작업까지 완료하고 나니 이제 정말 Hack Day가 시작된다는 것을 피부로 느낄 수 있었습니다(물론 기획은 개발 도중 시시각각 바뀌었지만요 😂).대망의 Hack Day 1일 차, 설레는 마음으로 오피스를 방문해 리셉션에서 출장 기간 동안 사용할 방문증을 배부 받았습니다. 저는 이번 출장에 Hack Day의 현장을 취재하고 담아오는 'LINE DEV 리포터즈'의 역할도 담당하고 있었기에 그 소임을 다하기 위해 다소 일찍 오피스에 도착한 편이었는데요. 벌써부터 몇몇 팀은 회의를 시작하고 있었습니다.아무래도 인원수가 많다 보니 행사장은 오피스 1개 층을 통째로 사용하고 있었고, 팀 간 공 간이 분리돼 있었습니다. 돌이켜 생각해 보니 Hack Day 내내 쾌적하다는 느낌을 받았는데요. 작년 대비 더 넓은 공간을 빌려 인구 밀집도를 낮춰서 개발에 몰두할 수 있는 환경이 제공됐기 때문이라고 생각합니다.매년 꾸준히 개최되는 행사인 만큼 전통이 하나 있습니다. 바로 모든 해커들이 모여서 개회 선언을 하는 것입니다. 1일 차 오전 9시, 모두가 한자리에 모여 크게 "Hack Day!"라고 외치며 이제 진짜로 해커톤이 시작됐음을 알렸습니다.개회식이 끝나자마자 모든 해커들은 지체 없이 자리에 앉아 개발에 착수했습니다. 도합 24시간이라는 시간은 길다면 길고 짧다면 짧은 시간으로, 한 끗 차이로도 완성도가 달라질 수 있기 때문에 모두가 'Perfect the Details'를 향해 달리기 시작했습니다. 행사장에 준비된 모니터와 곳곳에 배치된 화이트보드 등 주위의 모든 자원을 활용하는 모습이 인상 깊었습니다.끊이지 않는 개발, 그리고 끊이지 않는 간식저는 입사 이전에도 이곳저곳의 해커톤 행사에 다녀본 경험이 있는데요. 부끄럽지만 제가 생각하는 해커톤의 꽃은 바로 '끊이지 않는 간식'입니다. 사소해 보일 수 있지만 단기간에 폭발적으로 머리를 쓰는 만큼 당분과 카페인 섭취는
2025.08.07
emoji
좋아요
emoji
별로에요
logo
도메인 관리 자동화를 위한 ExternalDNS
안녕하세요. 여기어때컴퍼니 SRE팀에서 EKS(Elastic Kubernetes Service, AWS의 관리형 Kubernetes 서비스)를 담당하고 있는 젠슨입니다. 이번 글에서는 EKS 관리의 효율성을 높이기 위한 ExtenalDNS 활용에 대해 이야기 해보겠습니다.EKS 외부 에서 EKS 내부에 구동중인 Pod의 Application을 호출하기 위해서는 Ingress를 통해 접근해야 합니다. Ingress는 보통 ALB로 생성 되며 ALB의 DNS 정보를 통해 호출할 수 있습니다. DNS 정보는 internal-k8s-xxx-xxxxxxxxxx-xxxxxxxx.ap-northeast-2.elb.amazonaws.com 와 같은 형식이며 해당 DNS를 브라우저에 입력 후 호출하면 Pod로 생성된 Application에 접근할 수 있습니다.이러한 ALB의 DNS 주소를 사람이 매번 입력하여 접속하기는 어렵기 때문에 우리는 Route53과 같은 서비스를 이용하여 사용하기 쉬운 도메인 주소를 사용합니다. 위의 DNS 정보를 alias 형태로 Route53 에 등록하여 test.example.com과 같은 형태로 사용하는 것입니다.Ingress로 생성한 Host 주소가 몇개 없다면 Route53에 레코드를 등록하는 과정이 번거롭지 않지만 EKS에서 구동중인 Application의 숫자가 늘어날수록 사람이 직접 관리 하기가 힘들어지게 됩니다. 이렇게 번거롭고 어려운 도메인 관리를 ExternalDNS를 통해 자동으로 해결할 수 있습니다.즉, ExternalDNS는 Ingress나 Service의 Host에 선언한 도메인 주소들을 자동으로 Route53에 등록 및 관리해 주는 역할을 담당합니다.1. ExternalDNS 환경 구성ExternalDNS를 사용하기 전에 ExternalDNS에서 사용할 IAM Role과 Policy 를 생성해야 합니다. 일반적으로 여러 개의 AWS 계정을 사용 하더라도 보통 도메인은 1개의 계정에서 관리 합니다. 여기서는 도메인을 관리하는 별도의 AWS 계정이 있다는 가정하에 설명 하도록 하겠습니다.ExternalDNS 설치 계정EKS가 설치 되어 있고 ExternalDNS를 설치하는 계정 입니다.Policy Name : eks-external-dns-policyPolicy{ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Resource": "arn:aws:iam::{{도메인 관리 AWS Account ID}}:role/cross-account-external-dns-role" } ], "Version": "2012-10-17"}Role Name : eks-external-dns-role신뢰 관계{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow",
helm
kubernetes
8/6/2025
logo
도메인 관리 자동화를 위한 ExternalDNS
안녕하세요. 여기어때컴퍼니 SRE팀에서 EKS(Elastic Kubernetes Service, AWS의 관리형 Kubernetes 서비스)를 담당하고 있는 젠슨입니다. 이번 글에서는 EKS 관리의 효율성을 높이기 위한 ExtenalDNS 활용에 대해 이야기 해보겠습니다.EKS 외부 에서 EKS 내부에 구동중인 Pod의 Application을 호출하기 위해서는 Ingress를 통해 접근해야 합니다. Ingress는 보통 ALB로 생성 되며 ALB의 DNS 정보를 통해 호출할 수 있습니다. DNS 정보는 internal-k8s-xxx-xxxxxxxxxx-xxxxxxxx.ap-northeast-2.elb.amazonaws.com 와 같은 형식이며 해당 DNS를 브라우저에 입력 후 호출하면 Pod로 생성된 Application에 접근할 수 있습니다.이러한 ALB의 DNS 주소를 사람이 매번 입력하여 접속하기는 어렵기 때문에 우리는 Route53과 같은 서비스를 이용하여 사용하기 쉬운 도메인 주소를 사용합니다. 위의 DNS 정보를 alias 형태로 Route53 에 등록하여 test.example.com과 같은 형태로 사용하는 것입니다.Ingress로 생성한 Host 주소가 몇개 없다면 Route53에 레코드를 등록하는 과정이 번거롭지 않지만 EKS에서 구동중인 Application의 숫자가 늘어날수록 사람이 직접 관리 하기가 힘들어지게 됩니다. 이렇게 번거롭고 어려운 도메인 관리를 ExternalDNS를 통해 자동으로 해결할 수 있습니다.즉, ExternalDNS는 Ingress나 Service의 Host에 선언한 도메인 주소들을 자동으로 Route53에 등록 및 관리해 주는 역할을 담당합니다.1. ExternalDNS 환경 구성ExternalDNS를 사용하기 전에 ExternalDNS에서 사용할 IAM Role과 Policy 를 생성해야 합니다. 일반적으로 여러 개의 AWS 계정을 사용 하더라도 보통 도메인은 1개의 계정에서 관리 합니다. 여기서는 도메인을 관리하는 별도의 AWS 계정이 있다는 가정하에 설명 하도록 하겠습니다.ExternalDNS 설치 계정EKS가 설치 되어 있고 ExternalDNS를 설치하는 계정 입니다.Policy Name : eks-external-dns-policyPolicy{ "Statement": [ { "Action": "sts:AssumeRole", "Effect": "Allow", "Resource": "arn:aws:iam::{{도메인 관리 AWS Account ID}}:role/cross-account-external-dns-role" } ], "Version": "2012-10-17"}Role Name : eks-external-dns-role신뢰 관계{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow",
2025.08.06
helm
kubernetes
emoji
좋아요
emoji
별로에요
logo
Next.js 개발 중 마주한 Hydration Error와 원인
안녕하세요. 서비스웹개발팀의 헨비입니다. 이번 패키지 여행 상품 추가 프로젝트를 진행하면서, React, Next.js를 개발 했던 Front-End 개발자라면 익숙할 Next.js의 SSR Hydration Error를 다시 한번 겪게 되었습니다. 이번에는 단순 해결을 넘어, 원인과 동작 원리를 깊이 공부해 본 경험을 정리해보려 합니다.갑자기 발생한 “Hydration Error”1차 개발이 완료 된 후 Feature를 QA를 위해 Dev Server에 첫 배포 후 갑자기 Hydration Error가 발생했습니다.Hydration Error가 무엇일까?Hydration Error는 Next.js나 React SSR 환경에서, 클라이언트가 서버에서 렌더(render)된 HTML을 재사용(hydrate)할 때 서버와 클라이언트의 렌더링 결과가 다를 경우 발생하는 오류를 말합니다.출처 : https://dev.to/iamdevmarcos/react-hydration-explained-3lk0즉, 페이지의 HTML을 서버에서 미리 렌더링해 사용자에게 빠르게 보여준 뒤, 클라이언트에서 React가 이를 Takeover하는 과정에서 내용이 다르면, React는 이를 일관성 오류(inconsistency)로 간주하고 에러를 발생시킵니다.CSR(Client Side Rendering)만 사용하는 SPA에서는 이런 문제가 드물지만, Next.js처럼 SSR(Server Side Rendering)과 CSR을 혼합해 사용하는 경우, 서버에서 렌더링된 HTML과 클라이언트 렌더링 결과가 다르면 예상치 못한 UI 깨짐, 깜빡임(flicker), 기능 오류 등 심각한 사용자 경험 저하로 이어질 수 있습니다.비동기 처리 데이터, 시간, 랜덤 값, 브라우저 API(window, navigator) 등을 서버 렌더에서 그대로 사용할 경우, 서버와 클라이언트의 실행 환경 차이로 인해 hydration mismatch가 쉽게 발생합니다.원인을 찾아 떠나는 여행2달간 3명의 인원이 만든 대형 코드가 Local에서는 빌드와 테스트까지 모두 잘 되었지만, dev 환경에서 갑자기 에러가 발생하니 당황할 수밖에 없었습니다. 더군다나 다음 날부터 QA가 진행될 예정이었으니 말이죠.하지만 개발자라면 이런 에러를 마주했을 때 오히려 침착하게 문제를 디테일하게 파악하고, 이를 통해 더 성장할 수 있어야 합니다.local serve는 잘 되었다.local build + pm2도 잘 되었다.dev 환경 변수로 local serve, build도 잘 되었다.dev 서버에 배포만 하면 hydration error가 발생했다.dev 서버의 기존 코드에서는 문제가 없었다.결국 원인은 dev 서버에서 새로운 코드가 에러를 만들어내고 있다는 것이었습니다.Hydration Error 발생 시 행동 지침1. SSR과 CSR에서 값이 달라지는 코드 패턴 찾기서버는 브라우저 API(window, document, localStorage 등)를 모릅니다.useEffect나 useLayoutEffect 밖
nextjs
reactjs
8/6/2025
logo
Next.js 개발 중 마주한 Hydration Error와 원인
안녕하세요. 서비스웹개발팀의 헨비입니다. 이번 패키지 여행 상품 추가 프로젝트를 진행하면서, React, Next.js를 개발 했던 Front-End 개발자라면 익숙할 Next.js의 SSR Hydration Error를 다시 한번 겪게 되었습니다. 이번에는 단순 해결을 넘어, 원인과 동작 원리를 깊이 공부해 본 경험을 정리해보려 합니다.갑자기 발생한 “Hydration Error”1차 개발이 완료 된 후 Feature를 QA를 위해 Dev Server에 첫 배포 후 갑자기 Hydration Error가 발생했습니다.Hydration Error가 무엇일까?Hydration Error는 Next.js나 React SSR 환경에서, 클라이언트가 서버에서 렌더(render)된 HTML을 재사용(hydrate)할 때 서버와 클라이언트의 렌더링 결과가 다를 경우 발생하는 오류를 말합니다.출처 : https://dev.to/iamdevmarcos/react-hydration-explained-3lk0즉, 페이지의 HTML을 서버에서 미리 렌더링해 사용자에게 빠르게 보여준 뒤, 클라이언트에서 React가 이를 Takeover하는 과정에서 내용이 다르면, React는 이를 일관성 오류(inconsistency)로 간주하고 에러를 발생시킵니다.CSR(Client Side Rendering)만 사용하는 SPA에서는 이런 문제가 드물지만, Next.js처럼 SSR(Server Side Rendering)과 CSR을 혼합해 사용하는 경우, 서버에서 렌더링된 HTML과 클라이언트 렌더링 결과가 다르면 예상치 못한 UI 깨짐, 깜빡임(flicker), 기능 오류 등 심각한 사용자 경험 저하로 이어질 수 있습니다.비동기 처리 데이터, 시간, 랜덤 값, 브라우저 API(window, navigator) 등을 서버 렌더에서 그대로 사용할 경우, 서버와 클라이언트의 실행 환경 차이로 인해 hydration mismatch가 쉽게 발생합니다.원인을 찾아 떠나는 여행2달간 3명의 인원이 만든 대형 코드가 Local에서는 빌드와 테스트까지 모두 잘 되었지만, dev 환경에서 갑자기 에러가 발생하니 당황할 수밖에 없었습니다. 더군다나 다음 날부터 QA가 진행될 예정이었으니 말이죠.하지만 개발자라면 이런 에러를 마주했을 때 오히려 침착하게 문제를 디테일하게 파악하고, 이를 통해 더 성장할 수 있어야 합니다.local serve는 잘 되었다.local build + pm2도 잘 되었다.dev 환경 변수로 local serve, build도 잘 되었다.dev 서버에 배포만 하면 hydration error가 발생했다.dev 서버의 기존 코드에서는 문제가 없었다.결국 원인은 dev 서버에서 새로운 코드가 에러를 만들어내고 있다는 것이었습니다.Hydration Error 발생 시 행동 지침1. SSR과 CSR에서 값이 달라지는 코드 패턴 찾기서버는 브라우저 API(window, document, localStorage 등)를 모릅니다.useEffect나 useLayoutEffect 밖
2025.08.06
nextjs
reactjs
emoji
좋아요
emoji
별로에요
Copyright © 2025. Codenary All Rights Reserved.