🏢 LINE (LY Corporation) 기술 스택 분석
LINE이 아시아 2억+ 사용자에게 서비스를 제공하는 기술 스택 심층 분석 - Java, Kotlin, Verda 프라이빗 클라우드
🏢 LINE (LY Corporation) 기술 스택 분석
일본, 대만, 태국, 인도네시아 등 아시아 전역에서 2억+ 사용자에게 서비스를 제공하는 LINE의 기술 스택을 분석합니다. 2023년 10월 LINE과 Yahoo! JAPAN이 합병하여 LY Corporation으로 재출범
📊 회사 개요
서비스 규모
- 월간 활성 사용자: 2억+ 명 (일본, 대만, 태국, 인도네시아)
- 일일 메시지: 수십억 건
- 주요 서비스: LINE 메신저, LINE Pay, LINE MUSIC, LINE MANGA
- 합병 후: Yahoo! JAPAN, PayPay, ZOZO 등 통합
엔지니어링 조직
- 개발자: 10,000명+ (LY Corporation 전체)
- Verda 인프라 엔지니어: 60명+
- 내부 개발자 사용자: 2,500명+ (Verda 플랫폼)
- 오픈소스: 100+ 프로젝트 공개
기술적 도전과제
- 대규모 트래픽: 아시아 전역 수억 사용자
- 프라이버시: 엄격한 보안 및 개인정보 보호
- 글로벌 스케일: 다국가 인프라 운영
- 자체 클라우드: Verda 프라이빗 클라우드 운영
🏗️ 아키텍처 Overview
시스템 다이어그램
┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ LINE │────►│ Proxy │────►│ Backend │ │ Clients │ │ Layer │ │ Services │ └─────────────┘ └─────────────┘ └─────────────┘ │ │ │ │ ┌─────▼─────┐ ┌─────▼─────┐ │ │ Armeria │ │ Storage │ │ │ Gateway │ │ (HBase) │ │ └───────────┘ └───────────┘ │ │ │ └────────────────────┤ │ ┌─────▼─────┐ ┌─────▼─────┐ │ Verda │ │ Kafka │ │ Cloud │ │ Cluster │ └───────────┘ └───────────┘ Verda 프라이빗 클라우드 아키텍처
┌──────────────────────────────────────────────────────────┐ │ Verda Platform │ ├─────────────────────────┬────────────────────────────────┤ │ PaaS Layer │ IaaS Layer │ ├─────────────────────────┼────────────────────────────────┤ │ • Kubernetes (VKS) │ • Virtual Machines │ │ • MySQL Managed │ • Block Storage │ │ • Redis Managed │ • Object Storage │ │ • Elasticsearch │ • Shared Filesystem │ │ • Kafka Managed │ • Load Balancer │ │ • CI/CD Pipeline │ • NAT Gateway │ │ • Function (Serverless) │ • DNS as a Service │ └─────────────────────────┴────────────────────────────────┘ ↓ ┌─────────────────────────────┐ │ OpenStack Core │ │ • Nova (Compute) │ │ • Neutron (Network) │ │ • Cinder (Block Storage) │ │ • Designate (DNS) │ │ • Custom Nova Driver │ │ • SRv6 Overlay Network │ └─────────────────────────────┘ ↓ ┌─────────────────────────────┐ │ Physical Infrastructure │ │ 6,000+ Hypervisors │ │ 4M+ CPU Cores │ └─────────────────────────────┘ 핵심 설계 원칙
- Private Cloud First: 보안/프라이버시를 위한 자체 클라우드
- Microservices: 서비스별 독립 배포
- Polyglot: Java, Kotlin, Perl, Node.js 혼용
- Infrastructure as Code: Terraform 기반 관리
- Cloud Native: Kubernetes 기반 컨테이너화
기술 진화
- 2011: LINE 메신저 출시 (동일본 대지진 이후)
- 2014-2016: 글로벌 확장, 핀테크 진출
- 2016: Verda 프라이빗 클라우드 시작
- 2018-2020: LINE Pay, LINE Bank 확대
- 2023: Yahoo! JAPAN과 합병, LY Corporation 출범
- 2024-현재: 통합 플랫폼 구축
🧩 기술 스택 상세
Backend 기술
언어 및 프레임워크
| Technology | Usage | Notes | | ----------- | ------------- | --------------------------- | | Java | 핵심 백엔드 | 레거시 및 고성능 서비스 | | Kotlin | 신규 개발 | Perl 마이그레이션 대상 언어 | | Perl | 레거시 시스템 | 점진적 Kotlin 전환 중 | | Node.js | 웹 서비스 | Express, SSR | | Go | 인프라 도구 | Kubernetes 컨트롤러 |
Armeria - LINE의 오픈소스 RPC 프레임워크
// Armeria 서버 예시 import com.linecorp.armeria.server.Server; import com.linecorp.armeria.server.grpc.GrpcService; import com.linecorp.armeria.server.docs.DocService; public class LineServiceServer { public static void main(String[] args) { Server server = Server.builder() .http(8080) .https(8443) .tlsSelfSigned() // gRPC 서비스 .service( GrpcService.builder() .addService(new MessagingServiceImpl()) .addService(new UserServiceImpl()) .enableUnframedRequests(true) .build() ) // REST API .annotatedService("/api", new RestApiService()) // Thrift 서비스 (레거시 호환) .service("/thrift", THttpService.of(new ThriftServiceImpl())) // 문서화 .serviceUnder("/docs", DocService.builder().build()) // 헬스체크 .service("/health", HealthCheckService.of()) // 메트릭 .decorator(MetricCollectingService.newDecorator()) .build(); server.start().join(); } } // gRPC 서비스 구현 @GrpcService public class MessagingServiceImpl extends MessagingServiceGrpc.MessagingServiceImplBase { private final MessageRepository messageRepository; private final UserPresenceService presenceService; private final NotificationService notificationService; @Override public void sendMessage(SendMessageRequest request, StreamObserver<SendMessageResponse> responseObserver) { try { // 메시지 검증 validateMessage(request); // 메시지 저장 Message message = Message.builder() .id(generateMessageId()) .senderId(request.getSenderId()) .chatId(request.getChatId()) .content(request.getContent()) .type(request.getType()) .timestamp(Instant.now()) .build(); messageRepository.save(message); // 수신자에게 전달 List<String> recipients = getChatMembers(request.getChatId()); recipients.stream() .filter(userId -> !userId.equals(request.getSenderId())) .forEach(userId -> { if (presenceService.isOnline(userId)) { // 실시간 전송 pushToClient(userId, message); } else { // 푸시 알림 notificationService.sendPush(userId, message); } }); responseObserver.onNext(SendMessageResponse.newBuilder() .setMessageId(message.getId()) .setTimestamp(message.getTimestamp().toEpochMilli()) .setStatus(Status.SUCCESS) .build()); responseObserver.onCompleted(); } catch (Exception e) { responseObserver.onError( Status.INTERNAL .withDescription(e.getMessage()) .asRuntimeException() ); } } } Kotlin 백엔드 (LINE Creators Market)
// Kotlin + Spring WebFlux 서비스 @RestController @RequestMapping("/api/v1/stickers") class StickerController( private val stickerService: StickerService, private val authService: AuthService ) { @GetMapping("/{stickerId}") suspend fun getSticker( @PathVariable stickerId: Long, @RequestHeader("Authorization") token: String ): ResponseEntity<StickerDto> { val user = authService.validateToken(token) val sticker = stickerService.findById(stickerId) return if (sticker != null) { ResponseEntity.ok(sticker.toDto()) } else { ResponseEntity.notFound().build() } } @PostMapping suspend fun createSticker( @RequestBody request: CreateStickerRequest, @RequestHeader("Authorization") token: String ): ResponseEntity<StickerDto> { val creator = authService.validateCreator(token) val sticker = stickerService.create( Sticker( creatorId = creator.id, title = request.title, description = request.description, images = request.images, price = request.price, status = StickerStatus.PENDING_REVIEW ) ) return ResponseEntity .status(HttpStatus.CREATED) .body(sticker.toDto()) } @GetMapping("/trending") fun getTrendingStickers( @RequestParam(defaultValue = "0") page: Int, @RequestParam(defaultValue = "20") size: Int ): Flow<StickerDto> = flow { stickerService.getTrending(page, size) .collect { sticker -> emit(sticker.toDto()) } } } // 코루틴 기반 서비스 @Service class StickerService( private val stickerRepository: StickerRepository, private val searchService: ElasticsearchService, private val cacheService: RedisCacheService ) { suspend fun findById(id: Long): Sticker? { // 캐시 확인 cacheService.get<Sticker>("sticker:$id")?.let { return it } // DB 조회 val sticker = stickerRepository.findById(id) // 캐시 저장 sticker?.let { cacheService.set("sticker:$id", it, Duration.ofHours(1)) } return sticker } fun getTrending(page: Int, size: Int): Flow<Sticker> = flow { val ids = cacheService.zrevrange<Long>( "trending:stickers", (page * size).toLong(), ((page + 1) * size - 1).toLong() ) coroutineScope { ids.map { id -> async { findById(id) } }.awaitAll() .filterNotNull() .forEach { emit(it) } } } suspend fun search(query: String, filters: SearchFilters): List<Sticker> { return searchService.search( index = "stickers", query = SearchQuery( text = query, filters = mapOf( "status" to "APPROVED", "category" to filters.category, "priceRange" to filters.priceRange ), sort = filters.sortBy, size = filters.limit ) ) } } Frontend 기술
State of LY Frontend 2024
LY Corporation의 프론트엔드 기술 현황 (2024년 내부 조사 기반):
| Category | Technologies | | ------------- | ------------------------------ | | Framework | React, Vue.js, Next.js | | Language | TypeScript (주력) | | Styling | CSS Modules, Tailwind CSS | | State | Redux, Zustand, TanStack Query | | Testing | Jest, Vitest, Playwright | | Build | Vite, Webpack, Turbopack |
LINE 웹 클라이언트
// LINE 웹 클라이언트 (React + TypeScript) import React, { useEffect, useCallback } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; interface Message { id: string; chatId: string; senderId: string; content: string; type: "text" | "image" | "sticker" | "file"; timestamp: number; readBy: string[]; } interface ChatRoomProps { chatId: string; } export const ChatRoom: React.FC<ChatRoomProps> = ({ chatId }) => { const queryClient = useQueryClient(); const { user } = useAuth(); // 메시지 조회 const { data: messages, isLoading } = useQuery({ queryKey: ["messages", chatId], queryFn: () => fetchMessages(chatId), staleTime: 0, refetchOnWindowFocus: true, }); // 실시간 메시지 구독 useEffect(() => { const ws = new WebSocket(`wss://chat.line.me/ws/${chatId}`); ws.onmessage = (event) => { const message: Message = JSON.parse(event.data); queryClient.setQueryData(["messages", chatId], (old: Message[] = []) => [ ...old, message, ]); // 읽음 처리 if (message.senderId !== user.id) { markAsRead(message.id); } }; return () => ws.close(); }, [chatId, queryClient, user.id]); // 메시지 전송 const sendMutation = useMutation({ mutationFn: (content: string) => sendMessage(chatId, content), onMutate: async (content) => { // 낙관적 업데이트 const optimisticMessage: Message = { id: `temp-${Date.now()}`, chatId, senderId: user.id, content, type: "text", timestamp: Date.now(), readBy: [user.id], }; queryClient.setQueryData(["messages", chatId], (old: Message[] = []) => [ ...old, optimisticMessage, ]); return { optimisticMessage }; }, onError: (_, __, context) => { // 롤백 if (context?.optimisticMessage) { queryClient.setQueryData(["messages", chatId], (old: Message[] = []) => old.filter((m) => m.id !== context.optimisticMessage.id) ); } }, }); const handleSend = useCallback( (content: string) => { if (content.trim()) { sendMutation.mutate(content); } }, [sendMutation] ); if (isLoading) return <LoadingSpinner />; return ( <div className="chat-room"> <MessageList messages={messages || []} currentUserId={user.id} /> <MessageInput onSend={handleSend} disabled={sendMutation.isPending} /> </div> ); }; 인프라: Verda 프라이빗 클라우드
Verda 핵심 특징
- OpenStack 기반: Nova, Neutron, Cinder, Designate
- Kubernetes 지원: Rancher 기반 VKS (Verda Kubernetes Service)
- SRv6 네트워크: 테넌트 격리를 위한 커스텀 오버레이
- IaC: Terraform으로 인프라 관리
규모
- 하이퍼바이저: 1,400 → 6,000+ (2년간 4배 성장)
- CPU 코어: 400만+ 코어
- 내부 사용자: 2,500+ 개발자
- 인프라 엔지니어: 60명+
Terraform for Verda
# Verda 리소스 프로비저닝 예시 terraform { required_providers { verda = { source = "linecorp/verda" version = "~> 1.0" } } } provider "verda" { region = "jp-east-1" project_id = var.project_id } # Kubernetes 클러스터 resource "verda_kubernetes_cluster" "main" { name = "line-service-cluster" kubernetes_version = "1.28" master_config { instance_type = "m4.large" count = 3 } node_pool { name = "default-pool" instance_type = "m4.xlarge" min_size = 3 max_size = 100 autoscaling { enabled = true } } network { vpc_id = verda_vpc.main.id subnet_id = verda_subnet.private.id } } # 관리형 MySQL resource "verda_mysql" "main" { name = "line-db" engine_version = "8.0" instance_type = "db.r5.2xlarge" storage { type = "ssd" size = 500 } replication { mode = "async" replicas = 2 } backup { enabled = true retention_days = 30 window = "03:00-04:00" } } # 관리형 Redis resource "verda_redis" "cache" { name = "line-cache" version = "7.0" instance_type = "cache.r5.large" cluster_mode { enabled = true shards = 3 replicas_per_shard = 2 } } # 관리형 Kafka resource "verda_kafka" "events" { name = "line-events" version = "3.5" broker_config { instance_type = "kafka.m5.2xlarge" count = 6 } storage { size_per_broker = 1000 retention_hours = 168 } } 데이터 인프라
메시지 저장 및 처리
| Layer | Technology | Purpose | | ----------------- | ------------- | ------------------------- | | Primary DB | MySQL | 사용자, 채팅방 메타데이터 | | Message Store | HBase | 대량 메시지 저장 | | Cache | Redis | 세션, 최근 메시지 | | Search | Elasticsearch | 메시지 검색 | | Queue | Kafka | 이벤트 스트리밍 |
🔧 주요 오픈소스 프로젝트
LINE이 공개한 주요 오픈소스:
| Project | Description | Stars | | --------------------------------------------------------- | -------------------------- | -------- | | Armeria | 비동기 RPC/REST 프레임워크 | ⭐ 4.8k+ | | Central Dogma | 분산 설정 관리 서비스 | ⭐ 600+ | | LINE Bot SDK | LINE 봇 개발 SDK | ⭐ 700+ | | LINESimpleBLE | Bluetooth LE 라이브러리 | - | | Promgen | Prometheus 관리 도구 | ⭐ 800+ |
Armeria 상세
// Armeria의 주요 기능 // 1. 다중 프로토콜 지원 (HTTP/1, HTTP/2, gRPC, Thrift) Server server = Server.builder() .service("/grpc", GrpcService.builder() .addService(new MyGrpcService()) .build()) .service("/thrift", THttpService.of(new MyThriftService())) .annotatedService("/rest", new MyRestService()) .build(); // 2. 클라이언트 사이드 로드밸런싱 WebClient client = WebClient.builder("group:my-service") .endpointRemapper(endpoint -> Endpoint.of("service-discovery.local", 8080) .withWeight(100)) .decorator(RetryingClient.builder( RetryRule.failsafe()) .maxTotalAttempts(3) .newDecorator()) .decorator(CircuitBreakerClient.builder( CircuitBreaker.builder("my-service") .failureRateThreshold(0.5) .build()) .newDecorator()) .build(); // 3. 서킷 브레이커 + 리트라이 @Decorator(CircuitBreakerClientBuilder.class) @Decorator(RetryingClientBuilder.class) public interface MyServiceClient { @Get("/api/data") CompletableFuture<Data> getData(); } 💡 핵심 기술 인사이트
1. 프라이빗 클라우드 선택의 이유
- 보안: 엄격한 개인정보 보호 정책
- 비용: 대규모 운영 시 퍼블릭 클라우드 대비 효율적
- 커스터마이징: 특수 요구사항 대응 (SRv6 네트워크 등)
- 데이터 주권: 각국 규제 대응
2. Polyglot 전략
- Java: 안정성이 필요한 핵심 시스템
- Kotlin: 신규 개발 및 레거시 마이그레이션
- Perl → Kotlin: 점진적 현대화
- Node.js: 웹 SSR 서비스
3. OpenStack 커스터마이징
- 표준 Ironic 대신 자체 Nova 드라이버 개발
- SRv6 기반 L3 오버레이 네트워크
- Rancher 기반 Kubernetes (Magnum 대체)
📈 성과 & 지표
기술적 성과
- 인프라 성장: 2년간 4배 (1,400 → 6,000 하이퍼바이저)
- CPU 코어: 400만+ 코어 운영
- 개발자 지원: 2,500+ 내부 개발자
- 서비스 가용성: 99.9%+
비즈니스 영향
- MAU: 2억+ (일본, 대만, 태국, 인도네시아)
- LINE Pay 거래: 연간 수조 원
- LY Corporation 매출: 연간 1.7조 엔+ (합병 후)
🎓 LINE에서 배울 점
✅ 적용 가능한 패턴
- 프라이빗 클라우드: 보안/비용 최적화
- Armeria: 고성능 마이크로서비스
- Polyglot 마이그레이션: 점진적 현대화
- IaC: Terraform 기반 인프라 관리
- 오픈소스 문화: 핵심 기술 공개
❌ 주의사항
- 운영 복잡성: 대규모 프라이빗 클라우드 운영 부담
- 기술 부채: Perl 레거시 시스템
- 통합 과제: Yahoo! JAPAN 합병 후 시스템 통합
- 인재 확보: 전문 인력 필요
📚 추천 리소스
🔮 미래 전망
통합 후 집중 분야
- 통합 플랫폼: LINE + Yahoo! JAPAN 시너지
- AI 강화: 검색, 추천, 챗봇 고도화
- 핀테크: PayPay, LINE Pay 통합
- 이커머스: ZOZO, Yahoo! 쇼핑 연계
- 미디어: 뉴스, 엔터테인먼트 통합
기술 투자 영역
- LLM/AI: 생성형 AI 서비스
- 클라우드 확장: Verda 글로벌 확대
- Web3: 블록체인, NFT 플랫폼
- 보안: 제로 트러스트 아키텍처
- 개발자 경험: 내부 플랫폼 고도화
"기술로 CLOSING THE DISTANCE - 사람과 사람, 사람과 정보를 연결합니다."
- LY Corporation
마지막 업데이트: 2025-12-05