🏢 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