클래스룸 부가기능 만들기

이 가이드는 클래스룸 부가기능 둘러보기 시리즈의 첫 번째 둘러보기입니다.

이 둘러보기에서는 웹 애플리케이션을 개발하고 이를 클래스룸 부가기능으로 게시하기 위한 기반을 닦습니다. 이후 둘러보기 단계에서는 이 앱을 확장합니다.

이 둘러보기에서는 다음 작업을 완료합니다.

  • 웹 앱의 새 Google Cloud 프로젝트를 만듭니다.
  • 자리표시자 로그인 버튼이 있는 스켈레톤 웹 앱을 만듭니다.
  • 웹 앱의 비공개 Google Workspace Marketplace (GWM) 스토어 등록정보를 게시합니다.

완료되면 부가기능을 설치하고 클래스룸 부가기능 iframe에서 로드할 수 있습니다.

기본 요건

아래에서 언어를 선택하여 적절한 기본 요건을 확인하세요.

Python

Python 예에서는 Flask 프레임워크를 사용합니다. 개요 페이지에서 모든 둘러보기의 전체 소스 코드를 다운로드할 수 있습니다. 이 특정 둘러보기의 코드는 /flask/01-basic-app/ 디렉터리에서 찾을 수 있습니다.

필요한 경우 Python 3.7 이상을 설치하고 pip를 사용할 수 있는지 확인합니다.

python -m ensurepip --upgrade

또한 새 Python 가상 환경을 설정하고 활성화하는 것이 좋습니다.

python3 -m venv .classroom-addon-env
source .classroom-addon-env/bin/activate

다운로드한 예시의 각 둘러보기 하위 디렉터리에는 requirements.txt가 포함되어 있습니다. pip를 사용하여 필요한 라이브러리를 빠르게 설치할 수 있습니다. 다음을 사용하여 이 둘러보기에 필요한 라이브러리를 설치합니다.

cd flask/01-basic-app
pip install -r requirements.txt

Node.js

Node.js 예에서는 Express 프레임워크를 사용합니다. 개요 페이지에서 모든 둘러보기의 전체 소스 코드를 다운로드할 수 있습니다.

필요한 경우 NodeJS v16.13+를 설치합니다.

npm를 사용하여 필수 노드 모듈을 설치합니다.

npm install

Java

자바 예에서는 Spring Boot 프레임워크를 사용합니다. 개요 페이지에서 모든 둘러보기의 전체 소스 코드를 다운로드할 수 있습니다.

자바 11+가 머신에 아직 설치되어 있지 않으면 설치합니다.

Spring Boot 애플리케이션은 Gradle 또는 Maven을 사용하여 빌드를 처리하고 종속 항목을 관리할 수 있습니다. 이 예에는 Maven 자체를 설치하지 않고도 성공적인 빌드를 보장하는 Maven 래퍼가 포함되어 있습니다.

제공된 예를 실행하려면 프로젝트를 다운로드한 디렉터리에서 다음 명령어를 실행하여 프로젝트를 실행하기 위한 기본 요건을 갖추었는지 확인하세요.

java --version
./mvnw --version

또는 Windows에서는 다음 스크립트를 실행합니다.

java -version
mvnw.cmd --version

Google Cloud 프로젝트 설정

Classroom API에 대한 액세스 및 필수 인증 방법은 Google Cloud 프로젝트에서 제어합니다. 다음 안내에서는 부가기능과 함께 사용할 새 프로젝트를 만들고 구성하는 최소 단계를 안내합니다.

프로젝트 만들기

프로젝트 만들기 페이지로 이동하여 새 Google Cloud 프로젝트를 만듭니다. 새 프로젝트의 이름을 지정할 수 있습니다. 만들기를 클릭합니다.

새 프로젝트가 완전히 생성되는 데 몇 분 정도 걸립니다. 완료되면 프로젝트를 선택해야 합니다. 화면 상단의 프로젝트 선택기 드롭다운 메뉴에서 프로젝트를 선택하거나 오른쪽 상단의 알림 메뉴에서 프로젝트 선택을 클릭할 수 있습니다.

Google Cloud 콘솔에서 프로젝트 선택

GWM SDK를 Google Cloud 프로젝트에 연결하기

API 라이브러리 브라우저로 이동합니다. Google Workspace Marketplace SDK를 검색합니다. 결과 목록에 SDK가 표시됩니다.

Google Workspace Marketplace SDK 카드

Google Workspace Marketplace SDK 카드를 선택한 다음 사용 설정을 클릭합니다.

GWM SDK 구성하기

GWM에서는 사용자와 관리자가 부가기능을 설치할 수 있는 목록을 제공합니다. 계속하려면 OAuth 동의 화면과 GWM SDK의 앱 구성스토어 등록정보를 구성하세요.

사용자가 앱을 처음 승인할 때 OAuth 동의 화면이 표시됩니다. 사용 설정한 범위에 따라 앱에서 개인 정보 및 계정 정보에 액세스하도록 허용하라는 메시지가 사용자에게 표시됩니다.

OAuth 동의 화면 만들기 페이지로 이동합니다. 다음 정보를 입력합니다.

  • 사용자 유형외부로 설정합니다. 만들기를 클릭합니다.
  • 다음 페이지에서 필요한 앱 세부정보와 연락처 정보를 입력합니다. 승인된 도메인에서 앱을 호스팅하는 도메인을 제공합니다. 저장하고 계속하기를 클릭합니다.
  • 웹 앱에 필요한 OAuth 범위를 추가합니다. 범위와 그 목적에 관한 자세한 설명은 OAuth 구성 가이드를 참고하세요.

    Google에서 login_hint 쿼리 매개변수를 전송하려면 다음 범위 중 하나 이상을 요청해야 합니다. 이 동작에 대한 자세한 설명은 OAuth 구성 가이드를 참조하세요.

    • https://www.googleapis.com/auth/userinfo.email (이미 포함됨)
    • https://www.googleapis.com/auth/userinfo.profile (이미 포함됨)

    다음 범위는 클래스룸 부가기능에만 적용됩니다.

    • https://www.googleapis.com/auth/classroom.addons.teacher
    • https://www.googleapis.com/auth/classroom.addons.student

    또한 앱에서 최종 사용자에게 요구하는 다른 모든 Google API 범위를 포함합니다.

    저장 후 계속을 클릭합니다.

  • 테스트 사용자 페이지에 테스트 계정의 이메일 주소를 나열합니다. 저장하고 계속하기를 클릭합니다.

설정이 올바른지 확인한 다음 대시보드로 돌아갑니다.

앱 구성

GWM SDK의 앱 구성 페이지로 이동합니다. 다음 정보를 입력합니다.

  • 앱 공개 상태Private로 설정합니다. 이 설정은 테스트 및 개발 목적으로 적합하며 이 둘러보기에 적합합니다. 일반 대중이 부가기능을 사용할 준비가 된 경우에만 Public를 선택하세요.

  • 설치를 도메인 관리자로 제한하려면 Installation SettingsAdmin Only install로 설정합니다.

  • 앱 통합에서 클래스룸 부가기능을 선택합니다. 보안 연결 설정 URI를 입력하라는 메시지가 표시됩니다. 이 URL은 사용자가 부가기능을 열 때 로드될 것으로 예상되는 URL입니다. 이 둘러보기에서는 https://<your domain>/addon-discovery여야 합니다.

  • 허용된 연결 URI 프리픽스courses.*.addOnAttachments.createcourses.*.addOnAttachments.patch 메서드를 사용하여 AddOnAttachment에 설정된 URI를 검증하는 데 사용됩니다. 유효성 검사는 리터럴 문자열 프리픽스 일치 항목이며 현재 와일드카드 사용을 허용하지 않습니다. 지금은 비워 둘 수 있습니다.

  • 이전 단계의 OAuth 동의 화면에 표시된 것과 동일한 OAuth 범위를 추가합니다.

  • 개발자 링크에서 조직에 맞게 필드를 작성합니다.

스토어 등록정보

GWM SDK의 스토어 등록정보 페이지로 이동합니다. 다음 정보를 입력합니다.

  • 앱 세부정보에서 언어를 추가하거나 이미 나열된 언어 옆의 드롭다운을 펼칩니다. 부가기능의 GWM 스토어 등록정보 페이지에 표시됩니다. 완료를 클릭하여 저장합니다.
  • 부가기능의 카테고리를 선택합니다.
  • 그래픽 저작물에서 필수 입력란에 이미지를 제공합니다. 나중에 변경할 수 있으며 이전 단계에서 앱 공개 상태를 비공개로 설정한 경우 자리표시자일 수 있습니다.
  • 지원 링크에 요청된 URL을 제공합니다. 이전 단계에서 앱 공개 상태를 비공개로 설정한 경우 이러한 URL은 자리표시자가 될 수 있습니다.

게시를 클릭하여 설정을 저장합니다. 이전 단계에서 앱 공개 상태를 비공개로 설정하면 앱을 즉시 설치할 수 있습니다. 앱 공개 상태를 공개로 설정하면 GWM팀의 검토를 위해 앱을 보낸 후 앱을 설치할 수 있게 됩니다.

부가기능 설치하기

이제 GWM SDK의 스토어 등록정보 페이지 상단에 있는 링크를 사용하여 부가기능을 설치할 수 있습니다. 페이지 상단의 앱 URL을 클릭하여 등록정보를 본 다음 설치를 선택합니다.

기본 웹 앱 빌드

두 경로가 있는 스켈레톤 웹 애플리케이션을 설정합니다. 향후 둘러보기 단계를 통해 이 애플리케이션이 확장되므로 지금은 부가기능 /addon-discovery의 방문 페이지와 '회사 사이트'의 모의 색인 페이지 /을 만듭니다.

iframe 내 웹 앱의 예

다음 두 엔드포인트를 구현합니다.

  • /: 환영 메시지와 현재 탭과 부가기능 iframe을 모두 닫는 버튼을 표시합니다.
  • /addon-discovery: 환영 메시지와 함께 부가기능 iframe을 닫는 버튼과 새 탭에서 웹사이트를 여는 버튼을 표시합니다.

창이나 iframe을 만들고 닫는 버튼을 추가합니다. 이는 다음 둘러보기에서 승인을 위해 사용자를 새 탭으로 안전하게 표시하는 방법을 보여줍니다.

유틸리티 스크립트 만들기

static/scripts 디렉터리를 만듭니다. 새 파일 addon-utils.js를 만듭니다. 다음 두 함수를 추가합니다.

/**
 *   Opens a given destination route in a new window. This function uses
 *   window.open() so as to force window.opener to retain a reference to the
 *   iframe from which it was called.
 *   @param {string} destinationURL The endpoint to open, or "/" if none is
 *   provided.
 */
function openWebsiteInNewTab(destinationURL = '/') {
  window.open(destinationURL, '_blank');
}

/**
 *   Close the iframe by calling postMessage() in the host Classroom page. This
 *   function can be called directly when in a Classroom add-on iframe.
 *
 *   Alternatively, it can be used to close an add-on iframe in another window.
 *   For example, if an add-on iframe in Window 1 opens a link in a new Window 2
 *   using the openWebsiteInNewTab function above, you can call
 *   window.opener.closeAddonIframe() from Window 2 to close the iframe in Window
 *   1.
 */
function closeAddonIframe() {
  window.parent.postMessage({
    type: 'Classroom',
    action: 'closeIframe',
  }, '*');
};

경로 만들기

/addon-discovery/ 엔드포인트를 구현합니다.

Python

애플리케이션 디렉터리 설정하기

이 예에서는 애플리케이션 로직을 Python 모듈로 구조화합니다. 제공된 예에서 webapp 디렉터리입니다.

서버 모듈의 디렉터리(예: webapp)를 만듭니다. static 디렉터리를 모듈 디렉터리로 이동합니다. 모듈 디렉터리에도 template 디렉터리를 만듭니다. HTML 파일은 여기에 있습니다.

서버 모듈 빌드*

모듈 디렉터리에 __init__.py 파일을 만들고 다음과 같은 가져오기 및 선언을 추가합니다.

from flask import Flask
import config

app = Flask(__name__)
app.config.from_object(config.Config)

# Load other module script files. This import statement refers to the
# 'routes.py' file described below.
from webapp import routes

그런 다음 웹 앱의 경로를 처리할 파일을 만듭니다. 제공된 예에서 webapp/routes.py입니다. 이 파일에 두 경로를 구현합니다.

from webapp import app
import flask

@app.route("/")
def index():
    return flask.render_template("index.html",
                                message="You've reached the index page.")

@app.route("/classroom-addon")
def classroom_addon():
    return flask.render_template(
        "addon-discovery.html",
        message="You've reached the addon discovery page.")

두 경로 모두 각 Jinja 템플릿에 message 변수를 전달합니다. 이는 사용자가 도달한 페이지를 식별하는 데 유용합니다.

구성 만들기 및 파일 실행

애플리케이션의 루트 디렉터리에서 main.pyconfig.py 파일을 만듭니다. config.py에서 보안 비밀 키를 구성하세요.

import os

class Config(object):
    # Note: A secret key is included in the sample so that it works.
    # If you use this code in your application, replace this with a truly secret
    # key. See https://flask.palletsprojects.com/quickstart/#sessions.
    SECRET_KEY = os.environ.get(
        'SECRET_KEY') or "REPLACE ME - this value is here as a placeholder."

main.py 파일에서 모듈을 가져오고 Flask 서버를 시작합니다.

from webapp import app

if __name__ == "__main__":
    # Run the application over HTTPs with a locally stored certificate and key.
    # Defaults to https://localhost:5000.
    app.run(
        host="localhost",
        ssl_context=("localhost.pem", "localhost-key.pem"),
        debug=True)

Node.js

경로는 다음 줄과 함께 app.js 파일에 등록됩니다.

const websiteRouter = require('./routes/index');
const addonRouter = require('./routes/classroom-addon');

app.use('/', websiteRouter);
app.use('/addon-discovery', addonRouter);

/01-basic-app/routes/index.js를 열고 코드를 검토합니다. 최종 사용자가 회사 웹사이트를 방문하면 이 경로에 연결됩니다. 경로는 index Handlebars 템플릿을 사용하여 응답을 렌더링하고 템플릿에 titlemessage 변수가 포함된 데이터 객체를 전달합니다.

router.get('/', function (req, res, next) {
  res.render('index', {
    title: 'Education Technology',
    message: 'Welcome to our website!'
  });
});

두 번째 경로 /01-basic-app/routes/classroom-addon.js를 열고 코드를 검토합니다. 최종 사용자 방문 시 부가기능이 이 경로에 연결됩니다. 이 경로는 discovery Handlebars 템플릿과 추가로 addon.hbs 레이아웃을 사용하여 회사 웹사이트와 다르게 페이지를 렌더링합니다.

router.get('/', function (req, res, next) {
  res.render('discovery', {
    layout: 'addon.hbs',
    title: 'Education Technology Classroom add-on',
    message: `Welcome.`
  });
});

Java

자바 코드 예에서는 모듈을 사용하여 순차적 둘러보기 단계를 패키징합니다. 첫 번째 둘러보기이므로 코드는 step_01_basic_app 모듈에 있습니다. 모듈을 사용하여 프로젝트를 구현하지 않아도 됩니다. 둘러보기의 각 단계를 진행하면서 단일 프로젝트로 빌드하는 것이 좋습니다.

이 프로젝트 예에서는 컨트롤러 클래스 Controller.java를 만들어 엔드포인트를 정의합니다. 이 파일의 spring-boot-starter-web 종속 항목에서 @GetMapping 주석을 가져옵니다.

import org.springframework.web.bind.annotation.GetMapping;

클래스 정의 위에 Spring Framework 컨트롤러 주석을 포함하여 클래스의 목적을 나타냅니다.

@org.springframework.stereotype.Controller
public class Controller {

그런 다음 오류 처리를 위한 두 개의 경로와 추가 경로를 구현합니다.

/** Returns the index page that will be displayed when the add-on opens in a
*   new tab.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the index page template if successful, or the onError method to
*   handle and display the error message.
*/
@GetMapping(value = {"/"})
public String index(Model model) {
  try {
    return "index";
  } catch (Exception e) {
    return onError(e.getMessage(), model);
  }
}

/** Returns the add-on discovery page that will be displayed when the iframe
*   is first opened in Classroom.
*   @param model the Model interface to pass error information that's
*   displayed on the error page.
*   @return the addon-discovery page.
*/
@GetMapping(value = {"/addon-discovery"})
public String addon_discovery(Model model) {
  try {
    return "addon-discovery";
  } catch (Exception e) {
    return onError(e.getMessage(), model);
  }
}

/** Handles application errors.
*   @param errorMessage message to be displayed on the error page.
*   @param model the Model interface to pass error information to display on
*   the error page.
*   @return the error page.
*/
@GetMapping(value = {"/error"})
public String onError(String errorMessage, Model model) {
  model.addAttribute("error", errorMessage);
  return "error";
}

부가기능 테스트

서버를 시작합니다. 그런 다음 교사 테스트 사용자 중 하나로 Google 클래스룸에 로그인합니다. 수업 과제 탭으로 이동하여 새 과제를 만듭니다. 텍스트 영역 아래의 부가기능 버튼을 클릭한 다음 부가기능을 선택합니다. iframe이 열리고 부가기능이 GWM SDK의 앱 구성 페이지에서 지정한 첨부파일 설정 URI를 로드합니다.

수고하셨습니다 다음 단계인 Google SSO로 사용자 로그인을 진행할 준비가 되었습니다.