Programming Study/Java Spring

Livekit란? webRTC, Servlet, EL

Solbi Lee 2025. 6. 11. 10:06

https://livekit.io/

 

LiveKit | The all-in-one Voice AI platform

Build, deploy, and scale realtime agents. Open source. Enterprise grade.

livekit.io

- 확장 가능하고 유연한 webRTC SFU (Selective Forwarding Unit) 기반의 오픈소스 미디어 서버 

- 실시간 비디오, 오디오 및 데이터 통신을 위한 도구 제공 

- 개발자가 web RTC 애플리케이션을 쉽게 구축할 수 있도록 지원 

=> LiveKit은 실시간 영상통화, 음성채팅 기능을 직접 구현할 수 있게 도와주는 오픈소스 라이브러리

 

Q. 왜 필요한가 ?

요즘 앱들 보면 이런 기능 많죠?

1 :1 영상통화 기능 (틴더, 헬로톡)

라이브 방송 (인스타그램 라이브, 유튜브 라이브)

원격회의 (줌, 팀즈)

게임 내 음성채팅 (배틀그라운드, 포트나이트)

 

이런 걸 처음부터 직접 구현하려면 어렵고 복잡하기 때문에 Livekit 같은 webRTC 기반 미디어 서버 도구가 필요하다. 

 

어떻게 동작하나?

1. 사용자는 방(Room)에 입장 

Zoom 회의방처럼 방 단위로 사용자 연결을 관리함 

 

2. 마이크/카메라 데이터를 Livekit 서버로 보낸다 

LiveKit 서버는 이걸 다른 참가자들에게 전달해줌 (중개서버역할)

 

3. 실시간으로 영상과 음성이 동기화되어 전송됨 

참가자들은 서로 화면과 음성을 거의 지연없이 볼 수 있다 

 

예 : 내가 React로 만든 웹사이트에, 친구와 영상통화 기능을 넣고 싶다! 

-> Livekit을 쓰면 직접 Zoom같은 기능을 붙일 수 있음 

 

쓰이는 기술 

 

  • WebRTC – 실시간 영상·음성 전송 기술의 표준
  • SFU(Server-side Forwarding Unit) – 서버가 미디어 스트림을 중계
  • gRPC / HTTP – API 통신
  • Docker / Kubernetes – 서버 배포 환경
  • JWT 인증 – 사용자 권한 처리

webRTC란? 

Web Real-Time Communication의 줄임말로, 웹 브라우저끼리 실시간으로 영상, 음성, 데이터를 주고받을 수 있게 해주는 기술

즉, 설치 없이 웹에서 바로 화상통화, 음성통화, 채팅, 파일 전송 등을 가능하게 만들어주는 기술

 

주요 기능 

🎥 MediaStream 내 카메라, 마이크에서 영상·음성 가져오기
🔗 RTCPeerConnection 상대방과 직접 연결(P2P) 하기 위한 통신 통로
📩 RTCDataChannel 텍스트, 이미지, 파일 전송 등 데이터 교환용 채널

 

동작 흐름 

1. A가 B와 화상 통화를 하고 싶다 

2. A와 B는 먼저 신호를 주고 받아서 서로를 인지 

3. RTCPeerConnection을 통해 직접 연결 (p2p)을 만듦 

4. 연결되면 각자의 카메라/마이트를 상대방에게 보냄 

5. 동시에 데이터를 주고받을 수 있는 DataChannel도 쓸 수 있다. 


 

 

1. 📌 전체 목적 요약

이 코드는:

  • 사용자가 브라우저에서 /hello-servlet 주소로 요청하면,
  • 서버가 “Hello World!”라는 HTML을 UTF-8 인코딩으로 만들어 응답하는
  • 자바 서블릿 기본 구조입니다.

여기서 배우는 건 다음 4가지입니다:

학습 포인트 설명

🧠 서블릿 구조 클래스 생명주기, 메서드 흐름 이해
🌐 HTTP 요청·응답 처리 HttpServletRequest, HttpServletResponse 사용
🧾 HTML 응답 출력 Java 코드에서 직접 HTML 작성하는 방식
🌍 인코딩 처리 UTF-8 설정을 통해 한글 깨짐 방지

2. 📦 전체 코드 구조 한눈에 보기

package com.koreait.servlet;         // 패키지 선언

import java.io.*;                    // PrintWriter, IOException 등 I/O 클래스 사용
import jakarta.servlet.http.*;       // HttpServlet 등 서블릿 클래스 사용
import jakarta.servlet.annotation.*; // 서블릿을 애너테이션으로 등록

@WebServlet(name = "helloServlet", value = "/hello-servlet") // 브라우저 URL 매핑
public class HelloServlet extends HttpServlet {

    private String message; // 출력할 메시지를 저장할 변수

    public void init() {  // 서블릿 최초 1회 초기화
        message = "Hello World!";
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        response.setContentType("text/html; charset=UTF-8");  // 브라우저에 HTML 형식 + 한글 인코딩 설정
        response.setCharacterEncoding("UTF-8");               // 응답 문자 인코딩 설정

        try (PrintWriter out = response.getWriter()) {        // try-with-resources로 안전하게 출력 스트림 관리
            out.println("<!DOCTYPE html>");                   // HTML5 문서 타입
            out.println("<html><body>");
            out.println("<h1>" + message + "</h1>");          // 실제 출력할 메시지 삽입
            out.println("</body></html>");
        }
    }

    public void destroy() {  // 톰캣이 종료되거나 서블릿이 언로드될 때 호출됨
    }
}

3. 🧠 서블릿의 생명주기(Life Cycle)

서블릿은 자바 객체이지만 웹 서버(Tomcat)에서 다음처럼 특별하게 관리돼요:

단계 메서드 설명

초기화 init() 서블릿이 처음 로딩될 때 딱 1번 실행됨
요청 처리 doGet() / doPost() 클라이언트 요청이 들어올 때마다 호출
종료 destroy() 서버가 꺼지거나 서블릿이 사라질 때 호출

즉,

처음 한 번만 init()
요청이 올 때마다 doGet()
서버 종료되면 destroy()


4. 🧾 코드 한 줄씩 상세 해석

📌 패키지 및 import

package com.koreait.servlet;
  • 이 자바 파일이 속한 폴더 구조. src/com/koreait/servlet/HelloServlet.java
import java.io.*;
  • HTML을 출력할 때 필요한 PrintWriter, 예외처리용 IOException 등을 포함
import jakarta.servlet.http.*;
import jakarta.servlet.annotation.*;
  • 서블릿 생성을 위한 표준 라이브러리
    HttpServlet, HttpServletRequest, HttpServletResponse, @WebServlet 등 포함

📌 서블릿 등록

@WebServlet(name = "helloServlet", value = "/hello-servlet")
  • 이 클래스가 서블릿임을 서버에게 알리는 역할
  • 브라우저에서 http://localhost:8080/hello-servlet로 접속하면 이 서블릿이 실행됨

🧠 과거에는 web.xml로 등록했지만, 요즘은 이렇게 애너테이션 방식이 더 일반적


📌 변수 선언 & init()

private String message;
  • 클래스 멤버변수로, 이후 응답에 출력할 텍스트 저장소
public void init() {
    message = "Hello World!";
}
  • 서블릿이 최초로 로딩될 때 단 한 번만 실행
  • message 변수에 출력할 텍스트 저장

📌 doGet(): 요청 처리

public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
  • 클라이언트가 GET 방식으로 접속하면 실행됨
  • 보통 주소창에 입력하거나 a 태그, form의 기본 요청이 모두 GET

response.setContentType("text/html; charset=UTF-8");
response.setCharacterEncoding("UTF-8");
  • 응답의 타입을 HTML로 지정
  • 인코딩을 UTF-8로 설정하여 한글 깨짐 방지

try (PrintWriter out = response.getWriter()) {
  • 브라우저로 HTML을 출력할 스트림(PrintWriter) 을 생성
  • try-with-resources 문법을 사용 → 자동으로 닫힘 (자바 7부터 지원)

out.println("<!DOCTYPE html>");
out.println("<html><body>");
out.println("<h1>" + message + "</h1>");
out.println("</body></html>");
  • HTML 코드를 문자열로 작성해서 출력
  • message 변수에 저장된 "Hello World!"가 <h1> 태그로 표시됨

📌 destroy(): 정리 메서드

public void destroy() {
}
  • 톰캣 종료, 서블릿 언로드 시 실행됨
  • 보통 DB 연결 종료, 파일 닫기 등 리소스 정리에 사용

5. 💡 실무에서 이 구조가 왜 중요할까?

실무 포인트 설명

✅ 요청 처리 구조의 기본 모든 웹 로직은 결국 요청 → 처리 → 응답으로 구성됨
✅ REST API 개발 기반 이 구조 위에 GET, POST, PUT, DELETE 같은 HTTP 메서드를 구현
✅ 보안 처리, 세션 처리 request, response를 통해 로그인, 인증처리 가능
✅ JSP, HTML 분리 구조 학습 전 필수 화면과 로직을 나누기 전에 전체 흐름 파악 가능

6. 🚀 확장 아이디어 및 다음 학습 주제

학습 내용 예시

doPost() 구현 사용자 입력받기 (폼 데이터 처리)
request.getParameter() 클라이언트가 보낸 데이터 읽기
JSP 연동 RequestDispatcher.forward() 사용
DB 연동 JDBC로 메시지 DB에서 불러오기
session/cookie 관리 로그인 구현으로 확장 가능

✅ 최종 요약

이 서블릿 예제는 사용자의 GET 요청(/hello-servlet)을 받아 Hello World! 메시지를 UTF-8 HTML로 응답하는 기본 웹 서버 코드입니다. init() → doGet() → destroy() 흐름을 중심으로 서블릿의 동작 구조를 처음부터 끝까지 학습할 수 있으며, 실무에서의 웹 요청 처리의 핵심 개념을 포함하고 있습니다.

MyController.jsp

📌 한 줄 요약

.korea로 끝나는 URL 요청이 들어오면, MyController가 모든 요청을 가로채서 로그인/회원가입/정보수정 등 요청에 맞는 기능을 분기 처리합니다.


📁 이 코드의 실무 역할

역할 설명

URL 매핑 중앙 처리기 /join.korea, /login.korea 등 여러 URL을 하나의 컨트롤러에서 처리
GET/POST 통합 처리기 GET이든 POST든 하나의 doAction() 메서드에서 처리
한글 인코딩 처리 폼 전송 시 깨짐 방지
명령어 추출 로직 URL에서 어떤 기능을 실행할지 구분함 (command)

🧠 흐름 요약 다이어그램 (초보자용)

[ 브라우저 요청: /controller/join.korea ]
       ↓
[ MyController 서블릿 호출 ]
       ↓
[ doGet() 또는 doPost() 호출됨 ]
       ↓
[ 모든 요청은 doAction() 으로 위임됨 ]
       ↓
[ 요청 URL에서 command 추출 → 분기 처리 ]
       ↓
[ 회원가입, 로그인 등 기능 실행 ]

🧾 코드 해석 – 한 줄씩 뜯어보기


📦 1) 패키지 및 import

package com.koreait.servlet.controller;
  • 이 파일은 controller라는 역할(요청 분기처리)을 담당하는 폴더에 속해 있음
import jakarta.servlet.*;  
import jakarta.servlet.http.*;  
import jakarta.servlet.annotation.*;  
  • 자바 웹 개발에 필요한 서블릿 관련 클래스들:
    • HttpServletRequest: 요청정보
    • HttpServletResponse: 응답정보
    • @WebServlet: 서블릿 URL 매핑을 위한 애너테이션

🌐 2) URL 패턴 등록

@WebServlet("*.korea")

*.korea로 끝나는 모든 요청을 이 컨트롤러가 처리하게 만듦

예 의미

/controller/join.korea 회원가입 요청
/controller/login.korea 로그인 요청
/controller/modify.korea 정보 수정 요청

과거엔 web.xml에서 설정했지만, 요즘은 이렇게 애너테이션으로 관리


🔁 3) doGet, doPost → 공통 처리

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doAction(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    doAction(req, resp);
}
  • GET, POST 방식 모두 하나의 메서드로 통합 처리
protected void doAction(...) { ... }
  • 모든 요청은 이 doAction()에서 공통적으로 처리
    → 실무에서도 가장 자주 쓰는 방식!

✂️ 4) 요청 URI에서 기능명 추출

String uri = req.getRequestURI();       // 전체 요청 경로
String contextPath = req.getContextPath(); // 프로젝트 루트 경로
String command = uri.substring(contextPath.length()); // 기능만 추출

예를 들어, 주소가 이렇다면:

http://localhost:8080/servlet_war_exploded/controller/join.korea

변수 결과

uri /servlet_war_exploded/controller/join.korea
contextPath /servlet_war_exploded
command /controller/join.korea

즉, command를 통해 지금 어떤 기능을 요청했는지 파악할 수 있음!


🧠 5) 기능 분기 처리 로직

if (command.equals("/controller/join.korea")) {
    System.out.println("회원가입 작업!");
} else if (command.equals("/controller/login.korea")) {
    System.out.println("로그인 작업!");
} else if (command.equals("controller/info.korea")) {
    System.out.println("회원정보 작업!");
} else if (command.equals("controller/modify.korea")) {
    System.out.println("회원정보 수정 작업!");
}
  • command 값에 따라 각 기능을 조건문 if-else로 분기
  • 나중에는 System.out.println() 자리에 실제 로직 (예: DB 저장, 로그인 인증 등) 을 넣게 됨

🧪 실무에서 어떻게 확장하나?

확장 포인트 설명

JSP 연동 각 기능별 JSP 파일에 포워딩 (예: request.getRequestDispatcher().forward())
DB 연동 회원가입 시 DB에 사용자 정보 저장
Command 패턴 적용 각 기능별 클래스를 분리하여 유지보수 편하게
세션/쿠키 활용 로그인 인증 처리 및 세션 관리
예외처리 + 리다이렉트 잘못된 요청 처리 등 UX 향상

📎 연관해서 공부할 것

개념 이유

서블릿의 생명주기 왜 init(), doGet(), doPost()가 중요한지 이해
request.getParameter() 폼 데이터 처리 필수
RequestDispatcher vs sendRedirect 화면 이동 방식 차이
JSP와 MVC 패턴 뷰, 모델, 컨트롤러 구조 이해
Servlet + JDBC 연동 DB까지 연결하면 실무의 기본이 완성됨

✅ 최종 요약 

이 컨트롤러는 .korea로 끝나는 모든 요청을 하나의 서블릿에서 처리하며, GET, POST 요청을 구분하지 않고 doAction()으로 통합 처리합니다. URL에서 기능명을 추출하여 join.korea, login.korea 등으로 분기 처리하며, 실제 웹 시스템에서 로그인, 회원가입, 수정 등을 구현하는 기초 구조입니다.


EL (Expression Language)

JSP에서 자바 코드를 직접 쓰지 않고, 간단한 문법으로 데이터를 표현할 수 있도록 도와주는 언어 

request.getAttribute("name") <- 기존에 name 변수에 담아져 있던 거 가져왕! 

print 하려면 <%= request.getAttribute("name")%> 이렇게 썼었음 

근데 ${name} 이렇게 써도 됨 

 

내장 객체 우선순위 (이름이 동일했을 때, 아래의 순서대로 먼저 찍힘)

1. pageScope : 현재 페이지에서만 사용할 수 있는 속성 

2. requestScope : request 객체 

3. sessionScope 

4. applicationScope 

 

EL의 주요 기능 

${user} 

${user.userid} = ${user["userid"]}  <- user가 객체고 그 안에 필드가 존재한다면 이 형식도 가능함 

${user[0]} <- 배열이라면 이런식으로 접근 가능 

${user["key"]} : 맵

 

* 연산도 가능하다 

${price+1000} // 산술연산,비교연산 등 다 가능 (mod : 나머지)

 

* 삼항연산도 가능 

${price > 10000 ? "비싸다" : "싸다" }

&{empty name} // name이 null 또는 빈 문자열이면 true

 

예제

<%--
  Created by IntelliJ IDEA.
  User: dmddn
  Date: 2025-06-10
  Time: 오전 9:36
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>el</title>
</head>
<body>
<h2>el</h2>
<p>el 태그는 jsp의 표현식을 대체함</p>
<%-- http://localhost:8080/model_war_exploded/EL/1_basic.jsp --%>

${1+2}<br>
${'안녕하세요, EL'} <br>
${1 >= 2}<br>
${1<2 || 1>2}<br>
${1<2 && 1>2}<br>
${1==1 ? '같다' : '다르다'}<br>
${'김사과'=='김사과'}<br>
<%--같다고 나옴.--%>
${'김사과'eq'김사과'}<br>
<%--같다고 나옴.--%>
${5 gt 3}<br>
${3 lt 4}<br>

</body>
</html>

JSTL (JavaServer pages Standard Tag Library)

JSP에서 자주 사용하는 기능들을 표준 태그로 제공하는 라이브러리 

자바 코드 없이도 jsp에서 반복, 조건, 변수 설정, 출력 날짜 포맷 등을 html 태그처럼 사용할 수 있다.

 

<p>성인입니다.</p>
<% } %><c:if test="${age > 19}"><p>성인입니다.</p></c:if><% for(String item : list) { %>
<p>반복합니다.</p>
<% } %><c:forEach var="item" items="${list}"><p>반복합니다.</p></c:forEach>