API

Zoom API

hpehpeyy 2024. 9. 7. 21:53

 

Zoom API를 통한 회의 관리 (링크 제공)


1. Zoom App Marketplace

- Zoom App Marketplace에서 계정 생성 및 로그인

https://marketplace.zoom.us/develop/apps/AjVWH_seTZCLsnyQWFQ1tQ/credentials

 

- Bulid App에서 Server to Server OAuth App 방식으로 선택

 

- App Credentials 에서

Account ID 와 Client ID, Client Secret이 생성된걸 볼 수 있다.

 

- Information, Scopes 등을 설정해주고 활성화 시킨다 !

 


 

2. Eclipse

- Eclipse에서 Dynamic Project 생성

1. ZoomAuth.java

이 클래스는 Zoom의 OAuth 2.0 인증을 통해 액세스 토큰을 받아오는 기능을 제공

주요 메소드:
- getAccessToken(): Zoom의 OAuth 2.0 API를 호출하여 액세스 토큰을 가져온다.
- CLIENT_ID와 CLIENT_SECRET을 사용하여 기본 인증(Basic Auth)을 설정한다.
- grant_type=account_credentials와 account_id를 POST 요청의 본문으로 전달하여 액세스 토큰을 받는다.
- 받은 응답에서 access_token을 추출하여 반환한다.
public class ZoomAuth {

    private static final String TOKEN_URL = "";
    private static final String ACCOUNT_ID = "";
    private static final String CLIENT_ID = "";
    private static final String CLIENT_SECRET = "";
    private static List<ZoomMeeting> meetings = new ArrayList<>();

    // 모든 ZoomMeeting 객체를 반환하는 메소드
    public static List<ZoomMeeting> getMeetings() {
        return meetings;
    }

    // ZoomMeeting 객체를 추가하는 메소드
    public static void addMeeting(ZoomMeeting meeting) {
        meetings.add(meeting);
    }

    // Zoom OAuth 토큰을 가져오는 메소드
    public String getAccessToken() throws Exception {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(TOKEN_URL);

        String auth = CLIENT_ID + ":" + CLIENT_SECRET;
        String encodedAuth = Base64.getEncoder().encodeToString(auth.getBytes(StandardCharsets.UTF_8));
        post.setHeader("Authorization", "Basic " + encodedAuth);
        post.setHeader("Content-Type", "application/x-www-form-urlencoded");

        String body = "grant_type=account_credentials&account_id=" + ACCOUNT_ID;
        post.setEntity(new StringEntity(body));

        HttpResponse response = client.execute(post);
        HttpEntity entity = response.getEntity();
        String responseString = EntityUtils.toString(entity);

        // JSON 파서로 응답에서 access_token 추출
        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = mapper.readTree(responseString);
        return jsonNode.get("access_token").asText();
    }
}

 

 

2. ZoomMeeting.java

이 클래스는 Zoom 미팅을 생성하는 기능을 제공

주요 메소드:
- createMeeting(String accessToken, String topic, String password): Zoom API를 사용해 새로운 미팅을 생성한다.
- accessToken을 사용하여 Zoom API에 인증한다.
- topic(주제)과 password(비밀번호)를 사용해 미팅을 설정하고, 생성된 미팅의 정보를 JSON 형태로 받아서 처리한다.
- 이 메소드는 미팅의 join_url, meeting_id, start_url을 포함하는 ZoomMeetingResponse 객체를 반환한다.
public class ZoomMeeting {
    private String meetingId;
    private String password;
    private static final String CREATE_MEETING_URL = "";

    // Constructor
    public ZoomMeeting() {}

    public ZoomMeeting(String meetingId, String password) {
        this.meetingId = meetingId;
        this.password = password;
    }

    public ZoomMeetingResponse createMeeting(String accessToken, String topic, String password) throws Exception {
        CloseableHttpClient client = HttpClients.createDefault();
        HttpPost post = new HttpPost(CREATE_MEETING_URL);

        post.setHeader("Authorization", "Bearer " + accessToken);
        post.setHeader("Content-Type", "application/json");

        String jsonBody = "{"
                + "\"topic\": \"" + topic + "\","
                + "\"type\": 1," 
                + "\"password\": \"" + password + "\""
                + "}";

        post.setEntity(new StringEntity(jsonBody));

        HttpResponse response = client.execute(post);
        String responseString = EntityUtils.toString(response.getEntity());

        ObjectMapper mapper = new ObjectMapper();
        JsonNode jsonNode = mapper.readTree(responseString);

        String joinUrl = jsonNode.get("join_url").asText();
        String meetingId = jsonNode.get("id").asText();
        String startUrl = jsonNode.get("start_url").asText();

        return new ZoomMeetingResponse(joinUrl, meetingId, startUrl);
    }

    public String getMeetingId() {
        return meetingId;
    }

    public void setMeetingId(String meetingId) {
        this.meetingId = meetingId;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

 

 

3. ZoomMeetingResponse.java

이 클래스는 ZoomMeeting 클래스의 응답 데이터를 저장하는 역할

주요 필드:
- joinUrl : 참가자가 미팅에 참여할 수 있는 URL.
- meetingId : 생성된 미팅의 ID.- startUrl : 호스트가 미팅을 시작할 수 있는 URL.​

생성자 및 Getter 메소드:
- 생성자는 각 필드를 초기화하며, Getter 메소드는 각 필드의 값을 반환한다.
public class ZoomMeetingResponse {
    private String joinUrl;
    private String meetingId;
    private String startUrl;  // start_url 추가

    public ZoomMeetingResponse(String joinUrl, String meetingId, String startUrl) {
        this.joinUrl = joinUrl;
        this.meetingId = meetingId;
        this.startUrl = startUrl;  // start_url 초기화
    }

    public String getJoinUrl() {
        return joinUrl;
    }

    public String getMeetingId() {
        return meetingId;
    }

    public String getStartUrl() {
        return startUrl;
    }
}

 

 

4. ZoomController.java

이 서블릿은 Zoom 미팅을 생성하는 요청을 처리하고, 생성된 미팅 정보를 JSP 페이지로 전달

주요 메소드:
- doPost(HttpServletRequest request, HttpServletResponse response) : POST 요청을 처리하는 메소드
- topic, password 등의 요청 파라미터를 받아온다.

- ZoomAuth 클래스를 이용해 accessToken을 받아온다.
- ZoomMeeting 클래스를 이용해 새로운 Zoom 미팅을 생성한다.
- 생성된 미팅 정보를 ZoomAuth.addMeeting() 메소드를 통해 미팅 리스트에 추가한다.
- 미팅의 joinUrl, meetingId, startUrl 등을 Lecture.jsp 페이지에 전달한다.
public class ZoomController extends HttpServlet {

    public static ZoomMeeting getMeetingById(String meetingId) {
        List<ZoomMeeting> meetings = ZoomAuth.getMeetings();
        
        for (ZoomMeeting meeting : meetings) {
            if (meeting.getMeetingId().equals(meetingId)) {
                return meeting;
            }
        }
        
        return null;
    }

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        try {
            String topic = request.getParameter("topic");
            String description = request.getParameter("description"); // Retrieve the description
            String password = request.getParameter("password");

            ZoomAuth zoomAuth = new ZoomAuth();
            String accessToken = zoomAuth.getAccessToken();

            ZoomMeeting zoomMeeting = new ZoomMeeting();
            ZoomMeetingResponse zoomMeetingResponse = zoomMeeting.createMeeting(accessToken, topic, password);

            // Add the ZoomMeeting to the list
            ZoomAuth.addMeeting(new ZoomMeeting(zoomMeetingResponse.getMeetingId(), password));

            // Set attributes for the JSP page
            request.setAttribute("topic", topic); // Set the topic
            request.setAttribute("description", description); // Set the description
            request.setAttribute("joinUrl", zoomMeetingResponse.getJoinUrl());
            request.setAttribute("meetingId", zoomMeetingResponse.getMeetingId());
            request.setAttribute("startUrl", zoomMeetingResponse.getStartUrl());
            request.setAttribute("password", password);

            RequestDispatcher dispatcher = request.getRequestDispatcher("Lecture.jsp");
            dispatcher.forward(request, response);

        } catch (Exception e) {
            e.printStackTrace();
            response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "An error occurred while creating the meeting.");
        }
    }

}

 

 

5. JoinLecture.java

이 서블릿은 사용자가 미팅에 참여하려고 할 때 호출됨

주요 메소드:
- doPost(HttpServletRequest request, HttpServletResponse response) : 사용자가 제공한 meetingId와 password를 검증한다.
- meetingId로 미팅을 검색하고, 입력한 비밀번호가 일치하는지 확인한다.
- 일치하면 Zoom의 미팅 URL로 리디렉션(redirect)한다.
- 비밀번호가 틀리면 오류 메시지를 JSP 페이지(join.jsp)에 전달한다.
@WebServlet("/JoinLecture")
public class JoinLecture extends HttpServlet {
    private static final long serialVersionUID = 1L;

    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String meetingId = request.getParameter("meetingId");
        String password = request.getParameter("password");

        ZoomMeeting meeting = ZoomController.getMeetingById(meetingId);

        if (meeting != null && meeting.getPassword().equals(password)) {
            // Zoom 회의 참가 URL로 리디렉션
            String zoomJoinUrl = "https://zoom.us/j/" + meetingId + "?pwd=" + password;
            response.sendRedirect(zoomJoinUrl);  // 클라이언트를 줌 회의로 리디렉션
        } else {
            // 실패 시 오류 메시지를 join.jsp로 전달
            request.setAttribute("error", "Meeting ID or Password is incorrect. Please try again.");
            RequestDispatcher dispatcher = request.getRequestDispatcher("Join.jsp");
            dispatcher.forward(request, response);
        }
    }
}

 

 

6. CreateLecture.jsp

사용자가 새로운 강의를 생성할 수 있는 폼 페이지

주요 기능:
- 사용자는 topic, description, startTime, password를 입력한다.
- 이 폼은 ZoomController 서블릿으로 POST 요청을 보내며, 이 요청을 통해 미팅이 생성된다.
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Create New Lecture</title>
    <style>
        @font-face {
            font-family: 'NanumBarunpen';
            src: url('NanumBarunpenB.ttf') format('truetype');
        }

        body { 
            font-family: 'NanumBarunpen', Arial, sans-serif; 
            background-color: #f0f0f0; 
        }
        
        .container { 
            max-width: 600px; 
            margin: 50px auto; 
            padding: 20px; 
            background-color: #fff; 
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 
        }
        
        h2 { 
            text-align: center; 
            color: #333; 
        }
        
        form { 
            display: flex; 
            flex-direction: column; 
        }
        
        label { 
            margin-bottom: 5px; 
            color: #555; 
        }
        
        input, textarea { 
            margin-bottom: 15px; 
            padding: 10px; 
            font-size: 0.9em; 
            border: 1px solid #ddd; 
            border-radius: 5px; 
            font-family: 'NanumBarunpen';
            
        }
        
        button { 
            padding: 10px; 
            background-color: rgb(235, 212, 212); 
            color: black; 
            border: none; 
            border-radius: 5px; 
            font-size: 1em; 
            cursor: pointer; 
            font-family: 'NanumBarunpen';
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>Create a New Lecture</h2>
		<form action="createMeeting" method="post">
		    <label for="topic">Lecture Topic</label>
		    <input type="text" id="topic" name="topic" required>
		    
		    <label for="description">Description</label>
		    <textarea id="description" name="description" rows="4" required></textarea>
		    
		    <label for="password">Meeting Password</label>
		    <input type="text" id="password" name="password" required><br>
		    
		    <button type="submit">Create Lecture</button>
		</form>

    </div>
</body>
</html>

 

 

7. Join.jsp

사용자가 특정 미팅에 참여할 수 있도록 meetingId와 password를 입력하는 페이지

주요 기능:
- 사용자가 입력한 정보는 JoinLecture 서블릿으로 전송되어 미팅에 참여할 수 있다.
- 입력이 잘못된 경우 오류 메시지가 표시된다.
<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Join Lecture</title>
    <style>
        @font-face {
            font-family: 'NanumBarunpen';
            src: url('NanumBarunpenB.ttf') format('truetype');
        }

        body { 
            font-family: 'NanumBarunpen', Arial, sans-serif; 
            background-color: #f0f0f0; 
        }
        
        .container { 
            max-width: 600px; 
            margin: 50px auto; 
            padding: 20px; 
            background-color: #fff; 
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 
        }
        
        h2 { 
            text-align: center; 
            color: #333; 
        }
        
        form { 
            display: flex; 
            flex-direction: column; 
        }
        
        label { 
            margin-bottom: 5px; 
            color: #555; 
        }
        
        input, textarea { 
            margin-bottom: 15px; 
            padding: 10px; 
            font-size: 0.9em; 
            border: 1px solid #ddd; 
            border-radius: 5px; 
            font-family: 'NanumBarunpen';
            
        }
        
        button { 
            padding: 10px; 
            background-color: rgb(235, 212, 212); 
            color: black; 
            border: none; 
            border-radius: 5px; 
            font-size: 1em; 
            cursor: pointer; 
            font-family: 'NanumBarunpen';
        }
    </style>
</head>
<body>
    <div class="container">
        <h2>Join Lecture</h2>
        <% if(request.getAttribute("error") != null) { %>
            <div class="error"><%= request.getAttribute("error") %></div>
        <% } %>
        <form action="JoinLecture" method="post">
            <label for="meetingId">Meeting ID</label>
            <input type="text" id="meetingId" name="meetingId" required>
            
            <label for="password">Password</label>
            <input type="password" id="password" name="password" required>
            
            <button type="submit">Join</button>
        </form>
    </div>
</body>
</html>

 

 

8. Lecture.jsp

새로 생성된 Zoom 미팅의 정보를 보여주는 페이지

주요 기능:
- 생성된 미팅의 meetingId, joinUrl, startUrl 등을 표시한다.
- 호스트는 startUrl을 통해 미팅을 시작할 수 있다.
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Join Lecture</title>
    <style>
        @font-face {
            font-family: 'NanumBarunpen';
            src: url('NanumBarunpenB.ttf') format('truetype');
        }

        body { 
            font-family: 'NanumBarunpen', Arial, sans-serif; 
            background-color: #f0f0f0; 
        }
        
        .container { 
            max-width: 600px; 
            margin: 50px auto; 
            padding: 20px; 
            background-color: #fff; 
            box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 
            border-radius: 5px;
        }
        
        h2 { 
            text-align: center; 
            color: #333; 
            font-size: 1.5em;
        }
        
        .info-box {
            margin-bottom: 15px; 
            padding: 10px; 
            border: 1px solid #ddd; 
            border-radius: 5px;
            background-color: #fafafa;
            font-size: 1em;
            position: relative; /* 추가: 복사 버튼 위치 조정을 위해 */
        }

        .info-box p {
            margin: 0; 
            color: #555;
            word-wrap: break-word; /* 길이 긴 단어가 필요한 경우 다음 줄로 넘어가도록 설정 */
            overflow-wrap: break-word; /* 박스 경계를 넘어가는 경우 단어를 끊어서 표시 */
        }

        .info-box a {
            white-space: nowrap; /* URL이 한 줄에 표시되도록 설정 */
            overflow-wrap: break-word; /* 박스 경계를 넘어가는 경우 URL을 끊어서 표시 */
            word-wrap: break-word; /* URL이 긴 경우 적절하게 줄바꿈 */
        }

        .copy-button {
            position: absolute; /* 추가: 복사 버튼 위치를 절대 위치로 설정 */
            right: 5px; /* 오른쪽으로 10px 떨어뜨림 */
            top: 50%; /* 중간 위치 */
            transform: translateY(-50%); /* 버튼을 중간에 정확히 위치시킴 */
            background-color: #e0e0e0; 
            border: none; 
            padding: 5px 10px; 
            border-radius: 5px; 
            cursor: pointer; 
            font-family: 'NanumBarunpen';
            font-size: 0.7em; 
        }

        .button-container {
            text-align: center;
            margin-top: 20px;
        }

        a.button {
            padding: 10px 15px;
            background-color: rgb(235, 212, 212); 
            color: black; 
            border: none; 
            border-radius: 5px; 
            font-size: 1em; 
            text-decoration: none;
            cursor: pointer; 
            font-family: 'NanumBarunpen';
        }

        a.button:hover {
            background-color: rgb(220, 200, 200);
        }
    </style>
    <script>
        function copyToClipboard(text) {
            navigator.clipboard.writeText(text).then(function() {
                alert("Copied to clipboard: " + text);
            }, function(err) {
                alert("Failed to copy: " + err);
            });
        }
    </script>
</head>
<body>
    <div class="container">
        <h2>Join the Lecture</h2>

        <%
            String joinUrl = (String) request.getAttribute("joinUrl");
            String meetingId = (String) request.getAttribute("meetingId");
            String startUrl = (String) request.getAttribute("startUrl");
            String password = (String) request.getAttribute("password");
            String description = (String) request.getAttribute("description"); // Description 추가
            String topic = (String) request.getAttribute("topic"); // Lecture Topic 추가

            if (joinUrl != null && !joinUrl.isEmpty()) {
        %>
            <div class="info-box">
                <p><strong>Lecture Topic:</strong> <%= topic %></p> <!-- Topic 표시 -->
            </div>

            <div class="info-box">
                <p><strong>Description:</strong> <%= description %></p> <!-- Description 표시 -->
            </div>

            <div class="info-box">
                <p><strong>Meeting ID:</strong> <%= meetingId %></p>
                <button class="copy-button" onclick="copyToClipboard('<%= meetingId %>')">Copy</button> 
            </div>

            <div class="info-box">
                <p><strong>Join URL:</strong> <a href="<%= joinUrl %>" target="_blank"><%= joinUrl %></a></p>
                <button class="copy-button" onclick="copyToClipboard('<%= joinUrl %>')">Copy</button>
            </div>

            <div class="info-box">
                <p><strong>Meeting Password:</strong> <%= password %></p>
            </div>

            <div class="button-container">
                <a href="<%= startUrl %>" target="_blank" class="button">Start the Meeting</a>
            </div>
        <%
            } else {
        %>
            <div class="info-box">
                <p>No meeting is currently active. Please try again later.</p>
            </div>
        <%
            }
        %>
    </div>
</body>
</html>

 

 

9. web.xml

Java 웹 애플리케이션에서 서블릿, 필터, 리스너 등을 설정하는 데 사용된다.

웹 애플리케이션 루트 요소:
- web-app 요소는 웹 애플리케이션을 정의한다.
- version="3.1"은 Java EE 웹 애플리케이션 버전을 의미하며, xmlns 및 xsi:schemaLocation은 XML 네임스페이스와 XML 스키마의 위치를 지정한다.
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
    version="3.1">

 

서블릿 등록:
- ZoomController라는 서블릿을 설정한다.
- <servlet> 요소: ZoomController 서블릿을 정의한다. servlet-name은 서블릿의 이름이고, servlet-class는 이 서블릿이 구현된 클래스의 이름이다.
- <servlet-mapping> 요소: 이 서블릿에 대한 URL 매핑을 설정한다. /createMeeting URL 패턴으로 들어오는 요청은 ZoomController 서블릿이 처리한다.
<servlet>
    <servlet-name>ZoomController</servlet-name>
    <servlet-class>com.example.zoom.ZoomController</servlet-class>
</servlet>
<servlet-mapping>
    <servlet-name>ZoomController</servlet-name>
    <url-pattern>/createMeeting</url-pattern>
</servlet-mapping>

 

기본 페이지 설정:
- welcome-file-list 요소는 웹 애플리케이션의 루트 경로로 접근할 때 기본으로 로드될 파일을 지정한다. 여기서는 index.jsp가 기본 페이지로 설정되어 있다.
<welcome-file-list>
    <welcome-file>index.jsp</welcome-file>
</welcome-file-list>

 

오류 페이지 설정:
- 특정 오류 코드가 발생했을 때 표시될 페이지를 설정한다.
- 404 오류(페이지를 찾을 수 없음): error404.jsp 페이지로 이동한다.
- 500 오류(서버 오류): error500.jsp 페이지로 이동한다.
<error-page>
    <error-code>404</error-code>
    <location>/error404.jsp</location>
</error-page>
<error-page>
    <error-code>500</error-code>
    <location>/error500.jsp</location>
</error-page>

 

- 전체 코드

<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
	version="3.1">
	<!-- 서블릿 설정 -->
	<servlet>
		<servlet-name>ZoomController</servlet-name>
		<servlet-class>com.example.zoom.ZoomController</servlet-class>
	</servlet>
	<servlet-mapping>
		<servlet-name>ZoomController</servlet-name>
		<url-pattern>/createMeeting</url-pattern>
	</servlet-mapping>
	<!-- 기본 페이지 설정 -->
	<welcome-file-list>
		<welcome-file>index.jsp</welcome-file>
	</welcome-file-list>
	<!-- 오류 페이지 설정 (선택사항) -->
	<error-page>
		<error-code>404</error-code>
		<location>/error404.jsp</location>
	</error-page>
	<error-page>
		<error-code>500</error-code>
		<location>/error500.jsp</location>
	</error-page>
</web-app>

 

 

10. pom.xml

Maven 프로젝트의 핵심 구성 파일로, 프로젝트 빌드, 관리, 및 종속성 설정을 제어한다.

프로젝트 루트 요소:
- 이 요소는 Maven 프로젝트를 정의한다.
- modelVersion은 POM의 버전을 의미하며, xmlns 및 xsi:schemaLocation은 XML 네임스페이스와 XML 스키마의 위치를 지정한다.
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

 

프로젝트 식별자:
- groupID : 프로젝트를 식별하는 그룹 ID
- artifactID : 프로젝트의 이름
- version : 프로젝트의 버전을 나타냄. SNAPSHOT은 아직 릴리즈되지 않은 개발 중인 버전임을 나타낸다.
- packaging : 프로젝트의 패키징 타입을 지정. 여기서는 war(웹 애플리케이션 아카이브)로 설정되어 있다.
<groupId>com.example.zoom</groupId>
<artifactId>ZoomIntegration</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>

 

종속성 관리:
- org.apache.httpcomponents:httpclient : Apache HttpClient 라이브러리를 사용하여 HTTP 요청을 보낼 수 있도록 한다.
- com.fasterxml.jackson.core:jackson-databind : JSON 데이터를 처리하기 위해 사용된다. 이를 통해 JSON을 Java 객체로 변환 하거나 Java 객체를 JSON으로 변환할 수 있다.
- javax.servlet:javax.servlet-api : 서블릿 API를 제공한다. scope가 provided로 설정되어 있으므로, 이 라이브러리는 빌드할 때만 필요하며, 배포 환경 (예:Tomcat)에서는 제공되지 않는다.
<dependencies>
    <dependency>
        <groupId>org.apache.httpcomponents</groupId>
        <artifactId>httpclient</artifactId>
        <version>4.5.13</version>
    </dependency>
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.12.3</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>javax.servlet-api</artifactId>
        <version>4.0.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

 

빌드 플러그:
- maven-war-plugin : 이 플러그인은 프로젝트를 .war 파일로 패키징하는 데 사용된다.​
- failOnMissingWebXml : 이 속성을 false로 설정하면, web.xml이 없어도 빌드가 실패하지 않도록 한다. 이는 웹 애플리케이션에서 web.xml이 없어도 애노테이션 기반 설정을 사용할 수 있게 해준다.
<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>3.2.3</version>
            <configuration>
                <failOnMissingWebXml>false</failOnMissingWebXml>
            </configuration>
        </plugin>
    </plugins>
</build>

 

- 전체 코드

<project xmlns="http://maven.apache.org/POM/4.0.0"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.example.zoom</groupId>
	<artifactId>ZoomIntegration</artifactId>
	<version>1.0-SNAPSHOT</version>
	<packaging>war</packaging>
	<dependencies>
		<!--  Apache HttpClient for making HTTP requests  -->
		<dependency>
			<groupId>org.apache.httpcomponents</groupId>
			<artifactId>httpclient</artifactId>
			<version>4.5.13</version>
		</dependency>
		<!--  JSON processing  -->
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
			<version>2.12.3</version>
		</dependency>
		<!--  JSP and Servlets  -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>4.0.1</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-war-plugin</artifactId>
				<version>3.2.3</version>
				<configuration>
					<failOnMissingWebXml>false</failOnMissingWebXml>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

 


 

3. 전체 실행 과정

- 사용자가 CreateLecture.jsp 페이지에서 새로운 강의를 생성한다.

 

 

- ZoomController 서블릿은 입력된 정보를 사용해 Zoom API를 통해 미팅을 생성하고, 생성된 미팅 정보를 Lecture.jsp에 전달한다.

 

 

- 사용자가 특정 강의에 참여하려면 join.jsp에서 meetingId와 password를 입력한다.

 

- JoinLecture 서블릿이 입력된 정보를 검증하고, 올바르다면 해당 Zoom 미팅 URL로 리디렉션한다.

 

- 이렇게 각 컴포넌트가 협력하여 Zoom 강의를 생성하고 참여하는 기능이 구현된다.

 


 

4. 비교

1. Zoom API를 통한 회의 관리 (링크 제공)
개요
Zoom API를 사용하여 서버 측에서 회의를 생성하고 관리하는 방식
회의를 생성한 후, 생성된 회의 URL(링크)과 비밀번호 등을 사용자에게 제공하여 회의에 참여할 수 있도록 한다.

주요 특징
회의 관리 : 서버에서 회의를 생성하고, 생성된 회의 정보를 API를 통해 받아온다.
링크 제공 : 회의 링크(start_url, join_url)를 사용자에게 제공하여, 사용자는 이 링크를 통해 Zoom 클라이언트나 웹 브라우저에서 회의에 참여할 수 있다.
서버 측 구현 : 서버에서 Zoom API를 사용하여 회의 생성, 참가자 관리, 회의 일정 변경 등을 처리한다.

장점
광범위한 기능 : Zoom의 모든 API 기능을 사용할 수 있다. 예) 회의 예약, 녹화, 참가자 관리 등.
다양한 애플리케이션 : 웹, 모바일, 데스크탑 등 여러 플랫폼에서 동일한 회의 링크를 통해 회의에 참여할 수 있다.
유연한 통합 : API를 통해 회의 정보를 백엔드에서 관리할 수 있어, 기존 시스템과의 통합이 용이하다.

단점
링크 기반 참여 : 사용자가 회의에 참여하려면 Zoom 클라이언트나 웹 브라우저에서 링크를 클릭해야 한다.
서버 측 코드 구현 : 서버에서 API 호출을 관리하고, 회의 링크를 제공하는 로직을 구현해야 한다.
2. Zoom Web SDK
개요
Zoom Web SDK를 사용하여 웹 애플리케이션에 직접 Zoom 회의 기능을 통합하는 방식이다.
사용자는 웹 애플리케이션 내에서 Zoom 회의를 시작하고, 참여할 수 있다.​

주요 특징
직접 통합 : 웹 애플리케이션 내에서 Zoom 회의를 직접 시작하고, 참가할 수 있다.
웹 기반 참여 : 사용자가 브라우저에서 직접 Zoom 회의에 참여할 수 있으며, 별도의 Zoom 클라이언트를 설치할 필요가 없다.
Zoom UI 제공 : 기본적으로 Zoom의 UI를 사용하지만, SDK를 통해 일부 커스터마이징이 가능하다.​

장점
통합된 사용자 경험 : 사용자가 별도의 애플리케이션이나 링크를 통해 이동하지 않고, 웹 애플리케이션 내에서 바로 회의에 참여할 수 있다.
브라우저 기반 : 브라우저에서 직접 Zoom 회의를 실행할 수 있어, 사용자에게 보다 매끄러운 경험을 제공한다.
빠른 회의 시작 : 회의 링크를 따로 생성하고 공유할 필요 없이, SDK 내에서 바로 회의가 시작된다.​

단점
제한된 커스터마이징 : Zoom Web SDK의 UI는 커스터마이징이 가능하지만, 제한적일 수 있다.
브라우저 의존성 : 웹 브라우저의 성능에 따라 회의 품질이 달라질 수 있으며, 모든 브라우저에서 동일한 성능을 보장하기 어렵다.
웹 전용 : Web SDK는 웹 애플리케이션에만 통합될 수 있으며, 모바일이나 데스크탑 애플리케이션에는 적용할 수 없다.

 

 
Zoom API (링크 제공)
Zoom Web SDK
참여 방식
회의 링크를 제공하여 Zoom 클라이언트나 브라우저에서 참여
웹 애플리케이션 내에서 직접 회의 시작 및 참여
사용자 경험
사용자는 링크를 클릭하여 Zoom 클라이언트 또는 브라우저로 이동
사용자에게 매끄러운 통합된 경험 제공 (웹 내에서 회의 진행)
커스터마이징
서버 측 로직과 API를 통해 다양한 커스터마이징 가능
제한된 UI 커스터마이징 가능 (기본 Zoom UI 제공)
플랫폼 지원
웹, 모바일, 데스크탑 모두 지원
웹 브라우저에서만 지원
기술 복잡도
서버 측 API 통합 필요 (복잡하지만 강력)
클라이언트 측 SDK 통합 (상대적으로 간단)
성능
Zoom 클라이언트나 브라우저의 성능에 의존
브라우저의 성능에 크게 의존

 

Server-to-Server OAuth와 Web SDK 연동이 어려운 이유

  • Server-to-Server OAuth는 주로 API 호출을 위해 설계되었으며, 사용자 상호작용을 포함한 Web SDK와의 직접적인 연동을 지원하지 않는다.
  • Web SDK는 사용자 인증과 회의 관리 기능을 제공하는 데 API Key와 Secret을 사용하므로, Server-to-Server OAuth 앱과는 기본적으로 호환되지 않는다.

2023년도 부터 JWT 방식이 사라지고, 새로운 방식으로 Web SDK를 사용하는 자료나 정보들이 부족했다 !

다음에 기회가 된다면 천천히 다시 구현해보고싶당

Web RTC 방식도 사용해 보았는데 다음에 한번 정리를 해보겠다 ㅎ ㅎ 이상 -