요즘IT
위시켓
최근 검색어
전체 삭제
최근 검색어가 없습니다.

Google I/O는 구글에서 매년 5월 경 주관하는 개발자 컨퍼런스로, 구글에서 새롭게 발표하는 기술, 안드로이드 등의 최신 업데이트 사항들을 공유한다. 특히 올해는 구글이 ‘바드(Bard)’라는 인공지능 챗봇을 공개하면서 한국어를 우선 지원하겠다고 밝혀 큰 화제가 되기도 했다. 이외에도 모바일, 웹, AI, 클라우드 등의 다양한 분야와 이러한 기술들을 직접 학습해 보는 세션이 준비되었다. 이번 글에서는 그중 웹에 대해 다룬 주요 세션을 웹 프론트엔드 개발자 관점에서 살펴보고자 한다.

회원가입을 하면 원하는 문장을
저장할 수 있어요!

다음

회원가입을 하면
성장에 도움이 되는 콘텐츠를
스크랩할 수 있어요!

확인

개발

프론트엔드 개발자가 구글 I/O 2023 웹 세션에서 배운 것들

년차,
어떤 스킬
,
어떤 직무
독자들이 봤을까요?
어떤 독자들이 봤는지 궁금하다면?
로그인
구글 컨퍼런스
<출처:Google I/O 2023 홈페이지>

 

Google I/O는 구글에서 매년 5월 경 주관하는 개발자 컨퍼런스로, 구글에서 새롭게 발표하는 기술, 안드로이드 등의 최신 업데이트 사항들을 공유한다. 특히 올해는 구글이 ‘바드(Bard)’라는 인공지능 챗봇을 공개하면서 한국어를 우선 지원하겠다고 밝혀 큰 화제가 되기도 했다. 이외에도 모바일, 웹, AI, 클라우드 등의 다양한 분야와 이러한 기술들을 직접 학습해 보는 세션이 준비되었다. 이번 글에서는 그중 웹에 대해 다룬 주요 세션을 웹 프론트엔드 개발자 관점에서 살펴보고자 한다.

 

1. 웹에 관한 새로운 소식

  • 연사: Mariko Kosaka(Google Developer Relations Engineer)

 

 

세션 요약

웹은 항상 진화하고 있기 때문에 새로운 기능들을 매번 다 파악하는 건 어려운데, 가장 최근 두 버전에서 출시된 새로운 기능들을 중심으로 소개했다.

 

첫 번째로 dialog 요소가 생겼는데 팝업이나 모달 등에서 사용할 수 있다.

<dialog id="d">
  <form method="dialog">
    <p>Hi, I'm a dialog.</p>
    <button>ok</button>
  </form>
</dialog>

<button onclick="d.showModal()">
  Open Dialog
</button>

 

물론 기존에도 자바스크립트로 이런 다이얼로그를 구현할 수 있지만, 이 요소를 사용하면 focus 관리, tab 트래킹, 여러 개의 dialog를 동시에 노출할 때도 순서를 기억해서 수월하게 관리할 수 있다.

 

두 번째로 css transform 속성이 개별적으로 바뀌었다. 이전에는 하나의 transform에서 관리했다면, 이제는 각각 개별적으로 관리할 수 있게 되었다.

.target {
  transform: translateX(50%) rotate(30deg) scale(1.2)
}

.target {
  translate: 50% 0;
  rotate: 30deg;
  scale: 1.2;
}

 

이 방식의 장점은 translate, rotate, scale 등 각각의 요소들을 따로 필요한 곳에서 관리할 수 있게 되었다는 점이다.

 

세 번째로 viewport의 새로운 단위가 생겼다. 작은 viewport, 큰 viewport 등 모바일에서 각각 다른 viewport가 있는데, 이에 맞게 svh, lvh 등 적합한 도구를 사용할 수 있게 되었다.

 

네 번째로 깊은 복사(deep copy)를 할 수 있는 structuredClone API가 생겼다. 이제 JSON을 stringify(), parse() 해서 번거롭게 깊은 복사를 하지 않고, 깔끔하게 한 번에 할 수 있게 되었으며 퍼포먼스도 향상되었다.

const original = {id: 0, prop: {name: "Tom"}}
const deepCopy = JSON.parse(JSON.stringify(original));

const deepCopy = structuredClone(original);

 

다섯 번째로 focus-visible 의사 클래스가 css에 추가되었다. 하이퍼링크 등이 첨부된 텍스트에서 focus가 보여야 하는지 말아야 하는지는 사소하지만 필요한 디자인 의사 결정이다. focus-visible 클래스는 이를 구별해서 디자인할 수 있게 도와준다.

/* focus with tab key */
:focus-visible {
  outline: 5px solid pink;
}
/* mouse click */
:focus:not(:focus-visible) {
  outline: none;
}

 

여섯 번째로 Transform Stream 기능이 추가되었다. 데이터를 스트림 할 때 TransformStream()을 통해 read/write 가능한 데이터를 구분해서 스트림 처리해 줄 수 있게 되었다.

// Get from url1:
const response = await fetch(url1);
const {readable, writable} = new TransformStream();

// Compress the data from url1:
response.body
  .pipeThrough(new CompressionStream('gzip'))
  .pipeTo(writeable);

// Post to url2:
await fetch(url2, { method: 'POST', body: readable, });

 

마지막으로 importmap 타입이다. 외부 모듈을 코드에 삽입할 때, importmap을 사용하면 다음과 같은 매핑 로직을 가져올 수 있다.

<script type="importmap">
  {
    "imports": {
      "lodash": "https://unpkg.com/lodash@4.17.21/lodash.js",
      "util": "./modules/util.js"
    }
  }
</script>

<script type="module">
  import _ from "lodash";
  console.log(_.map([1, 2, 3], (n) => n * 2));
</script>

 

느낀 점

웹은 우리가 매일 쓰는 만큼 컴퓨터 공학 분야에서는 나름 역사가 긴 편에 속하고, 어느 정도 성숙기에 접어든 기술이라고 볼 수 있다. 그래서 새로운 기능이 나와도 간과하기가 쉽다. 그러나 주기적으로 웹에서 생기는 이러한 변화들을 살펴보면, 웹에서 어떤 기능들이 필요하고 또 개발자들이 어떤 고민을 했는 알 수 있다. 따라서 주기적으로 팔로업하되, 기존에 사용하던 도구들을 함께 고민해 본 후 도입하는 것이 좋겠다.

 

 

2. 안드로이드 기반 웹 관련 새로운 소식

  • 연사: Adriana Jara Salazar(Senior Developer Relations Engineer for Chrome), Sebastian Benz(Developer Relation Engineer)

 

 

세션 요약

이번 세션에선 안드로이드 웹뷰(Webview)와 관련된 최신 업데이트 사항들을 소개했다.

 

첫 번째로 프라이버시와 관련하여 X-Requested-With=<APK NAME>이 사라졌다. 기존에는 웹뷰에서 서버로 보내는 모든 요청에서 헤더에 APK 이름을 담아서 보내야 했다. 이 헤더는 deprecated 되었으며, 이를 해결하기 위해 새로운 opt-in API가 만들어졌다. 이 API를 통해 선택적으로 특정 오리진에만 헤더를 담아 보낼 수 있게 되었다.

WebSettingsCompat.setRequestedWithHeaderOriginAllowList(
  demoWebview.getSetting(), Collections.singleton("https://example.com")
);

 

두 번째는 웹뷰가 크롬 오리진 시범운영(Origin Trials)를 지원한다는 것이다. 이 기능을 통해 웹뷰에서도 새로운 기능이 모든 유저에게 배포되기 전에 시범적으로 운영을 해볼 수 있게 되었다.

 

세 번째로 넓은 기기를 사용하는 경우, 웹뷰에서도 이미지 드래그 앤 드롭(Drag’n Drop)이 가능해졌다. 화면을 분할해서 사용하는 경우, 웹뷰에서 앱으로 이미지를 드래그 앤 드롭 할 수 있다.

<application...>
  ...
  <provider
    android:authorities="com.example.webviewdemo.DropDataProvider"
    android.name="androidx.webkit.DropDataContentProvider"
    android:exported="false"
    android:grantUriPermissions="true" />
</application>

 

네 번째로 웹뷰에서 손글씨 쓰기와 HTML Input 필드 지원이 가능해졌다. 손글씨 쓰기는 삼성 휴대폰 기기의 ONE UI에서 대부분 가능하다.

 

다섯 번째로 Jetpack 자바스크립트 엔진을 통해 웹 콘텐츠를 보여주는 것 없이 자바스크립트 앱을 실행시킬 수 있게 되었다. 예를 들어 웹과 모바일 앱 간의 비즈니스 로직을 주고받아야 할 때, Jetpack 엔진을 통해 수월하게 할 수 있다. 아직 알파버전이며 웹뷰 인스턴스를 생성하지 않고, 자바스크립트와 WASM 코드를 연산한다. Jetpack 엔진을 사용하면 자바스크립트를 다른 프로세스로 실행하기 때문에 더 안전하고 안정되게 관리할 수 있다. 게다가 웹뷰 인스턴스에 비해 훨씬 적은 리소스를 쓴다는 장점이 있다.

 

여섯 번째로 커스텀 탭 기능이 있다. 커스텀 탭을 통해 사용자가 앱 안의 콘텐츠를 안전하고, 편리하게 볼 수 있게 만들었다. 기본 브라우저가 있고, 쿠키로 정보를 공유하기 때문에 사용자는 재로그인을 할 필요가 없다. 기본 브라우저가 크롬이면 커스텀 탭은 크롬이며, 기본 브라우저가 파이어폭스면 커스텀 탭도 파이어폭스가 된다. 커스텀 탭의 높이를 조절할 수가 있어서 부분적으로만 웹뷰를 보는 것도 가능하다.

CustomTabsSession customTabsSession;
// ...
CustomTabsIntent customTabsIntent = new 
CustomTabsIntent.Builder(customTabsSession)
.setInitialActivityHeightPx(500).setCloseButtonPosition(CustomTabsIntent.CLOSE_BUTTON_POSITION_END).build();

customTabsIntent.launchUrl(context, Uri.parse(url));

 

너비가 넓은 태블릿의 경우는 좌우로 스크린을 분할할 수 있는데, 이 기능은 현재 카나리에 있고 Q3에 릴리즈가 될 예정이다. 크롬 팀은 커스텀 탭에서 세션 기반으로 얼마나 유저가 해당 페이지에 머물렀는지를 파악하고, 이에 따라 어떤 커스텀 탭을 보여줄지를 결정한다. 스크롤 방향, 최대 스크롤 퍼센트 증가, 세션 시작과 끝 등의 신호를 통해 이에 관여하는 것이다.

 

웹앱(WebApp)에 대한 업데이트도 소개했는데, PWA를 통해 웹을 앱과 같이 배포할 수 있다. 이는 웹을 안드로이드에 들어오게 할 수 있는 또 다른 방법이다. 기존에는 안드로이드에 웹앱 설치 시 서비스 워커가 필요했는데 이제는 없어도 설치가 가능해졌다. 서비스 워커 패치 핸들러가 설치 과정에서 사라졌기 때문이다.

 

마지막으로 TWA(Trusted Web Activity)의 개념에 대해 살펴봤는데, TWA를 통해 웹 콘텐츠를 앱에 풀 스크린으로 보이게 할 수 있다. 웹앱을 플레이스토어에 올릴 수 있고, 웹 풀스크린을 안드로이드에서 사용할 수도 있다.

 

느낀 점

안드로이드에서 웹뷰 형태로 기능 개발을 해야 할 때가 많은데, 이와 관련된 레퍼런스나 사례가 상대적으로 부족해서 개발자들이 어려움을 겪었다. 이번 세션에서 다룬 ‘커스텀 탭’ 기능은 다양한 디바이스에서 여러 웹뷰를 띄워 사용해야 하는 서비스 개발 시 유용하게 쓰일 것으로 보인다. 웹뷰를 안드로이드와 같은 네이티브 환경에 가깝게 만들려는 팀의 고민이 느껴진 세션이다.

 

 

3. 플러터의 웹 지원 개선

  • 연사: Kevin Moore(Flutter Product Manager)

 

 

세션 요약

플러터로 배포된 웹은 지난 일 년간 두 배 이상 증가했다. 이번 세션에서는 웹을 위해 플러터에서 새롭게 출시한 기능들과 앞으로 플러터에서 새롭게 출시할 기능에 대해 다뤘다.

 

첫 번째로 앱 로드 시간이 빨라졌다. CanvasKit와 Font의 파일 사이즈가 3.7과 3.10을 비교했을 때 각각 절반, 1/40 정도로 줄었다. 이로 인해 전체 로드 시간이 42% 정도 단축되었다.

 

웹 경험도 더 나아졌다. 3.7에서는 전체 창이나 iframe에서 사용의 한계가 있었다. 3.10에서는 요소 임베딩(Element Embedding)이 추가되었다. 이는 플러터가 임의의 HTML 요소에서 실행되며, 자바스크립트로 동기적인 상호작용을 할 수 있음을 의미한다.

<script>
window.addEventListener("load", function (ev) {
  _flutter.loader.loadEntrypoint({
    onEntrypointLoaded: async function (engineInitializer) {
    let appRunner = await engineInitializer.initializeEngine({
      hostElement: document.querySelector("#flutter_target")
    });
    await appRunner.runApp();
  });
});
</script> 

 

플러터 웹은 플러터와 웹에서의 좋은 경험들을 지향하며, 플러터에서 지원하는 쉐이더(shader)의 GPU 가속화 기능은 이제 3.10 버전에서 웹앱으로도 지원한다. 코드 변경 없이 앱에서 웹으로 같은 기능을 구현할 수 있다. 

 

앞으로 3.10 이후 버전에서 플러터가 지향하는 방향도 소개되었는데, 웹어셈블리는 구글 지도나 어스, 텐서플로 같은 라이브러리를 만드는 데 사용되었다. 플러터는 오랫동안 WASM을 CanvasKit 등의 모듈을 만드는데 사용했다. 지금까지 플러터 웹은 다트(Dart) 코드를 컴파일 했지만, 이제는 성능 개선을 위해 웹어셈블리 코드로 전환을 고려하고 있다. 

 

웹어셈블리는 다트에 비해 가비지 콜렉션(GC) 기능이 부족하지만, WASM GC 기능이 추가되어 크로미움과 파이어폭스 브라우저에서 사용할 수 있게 되었다. 플러터의 목표는 WASM과 자바스크립트 컴파일을 동시에 포함하는 프로덕션 런타임을 만드는 것이다. 이 기능이 생긴다면 WASM을 쓰기 위해 브라우저에서 추가적으로 해야 할 일은 거의 없을 것이다.

 

느낀 점

플러터 팀에서 드라마틱한 성능 개선을 이뤄낸 부분이 인상적이었다. 아무래도 하이브리드 앱 시장 경쟁에서 확실한 우위를 점하기 위해 압도적인 퍼포먼스 향상이 필요했던 것으로 보인다. 플러터 웹은 모바일과 웹의 장점들을 가지고, 개발자의 생산성을 끌어올릴 수 있는 방향으로 지속적인 개선이 이뤄질 것으로 보인다. 마지막에 WASM과 자바스크립트를 동시에 실행할 수 있는 런타임을 만들겠다는 미래 목표를 밝힌 것도 멋있었다. 

 

 

4. 크롬 개발자 도구가 개발자의 디버깅을 효과적으로 지원하는 방법

  • 연사: Micheal Hablich(Chrome Team Product Manager)

 

 

세션 요약

이번 세션에서는 웹 개발자가 크롬 개발자 도구를 통해 디버깅을 효과적으로 하는 방법을 소개했다. 지난 15년간 웹 생태계는 엄청나게 복잡해졌다. 앵귤러, 리액트, 뷰 등의 프레임워크의 등장을 중심으로 웹팩(Webpack), 소스 맵 등의 도구들, 바벨(Babel)과 같은 트랜스파일러 등 수많은 도구들이 생겨났다. 크롬 팀은 이러한 복잡한 웹 개발 환경에서 개발자가 디버깅할 때 어떤 지점에서 어려움을 느꼈는지에 집중했다.

 

도쿄 시부야
현대 웹은 도쿄 시부야 거리처럼 많은 데이터들을 다양한 방향으로 처리하면서 질서를 유지해야 한다.<출처:arigato travel>

 

먼저 개발자 도구에서 소스 패널을 펼치면 혼란스러운 파일들이 보인다. 크롬 팀에서는 작성된 뷰(Authored View), 배포된 뷰(Deployed View) 개념을 도입했다. 소스 맵이 감지되면 자동으로 보이게 옵션을 설정할 수 있다. 서드 파티 라이브러리를 사용하는 경우에도, 서드 파티 코드를 제외한 본인의 소스 코드만 보고 싶을 때가 있을 것이다. 이때 서드 파티 코드를 무시할 수 있는 기능이 크롬 개발자 도구에 추가되었다.

 

다음으로 우리가 작성하는 코드와 전달되는 코드에 변형이 일어나는 경우가 있는데, 이는 소스 맵으로 불리는 도구에 의해 일어난다. 소스 맵의 동작 방식은 많은 개발자들이 모르는 채로 이루어진다. 만약 소스 맵의 동작을 알고 싶다면 개발자 리소스(Developer Resources) 탭에서 확인할 수 있다.

 

현대 웹은 콘솔 로그만 가지고 디버깅을 하기 어렵다. 더 효과적인 디버깅을 위해 중단점(breakpoints), 로그포인트(logpoint) 등을 사용할 수 있다. 중단점은 조건을 주어 특정 상황에서만 동작이 멈추게 할 수도 있다. 또한 어떤 버그 상황을 동료에게 전달하고 싶을 때, 브라우저의 녹화 기능을 사용해 보자. 브라우저에서 상호작용을 저장할 수 있고, 추출도 다양한 방식으로(JSON, Puppeteer 등) 할 수 있다.

 

느낀 점

이번 세션은 사실 현업에서 일하는 개발자라면 대부분 아는 내용이라서 조금 아쉬웠다. 내가 모르는 크롬 개발자 도구의 새로운 기능이나 숨겨진 기능을 설명해 줄 거라 기대했는데 그런 부분은 거의 없었다. 개론적인 이야기가 많아서 경험이 있는 웹 개발자보다는 웹 개발이 처음이거나, 관심 있는 분들이 들어보면 좋을 것이다.

 

 

5. WebDriver BiDi: 브라우저 간 자동화의 미래

  • 연사: Jecelyn Yeen(Chrome DevRel Engineer)

 

 

세션 요약

이번 세션에서는 새로운 브라우저 자동화 프로토콜인 WebDriver BiDi를 어떻게 사용할 수 있는지에 대해 알아보았다.

 

브라우저 자동화는 크게 고수준과 저수준으로 나뉜다. 고수준은 브라우저 안에서 자바스크립트를 실행하며, 저수준은 브라우저 밖에서 원격 커맨드를 통해 실행한다. 예를 들어, 사이프레스(Cypress)는 시스템 API를 레버리지하여 Node.js를 통해 브라우저 안에서 직접 테스트를 한다. 하지만 여러 탭을 사용하거나, 아이프레임 등을 사용하는 경우 더 깊숙이 들어가서 테스트를 해야 하기 때문에 저수준으로 들어가야 한다. 이때 사용하는 두 가지 도구가 기존 웹 드라이버 프로토콜과 크롬 개발자 도구 프로토콜(CDP)이다.

 

기존 웹 드라이버는 자동화 도구와 HTTP 통신으로 명령을 주고받고, 브라우저와는 내부 특정 프로토콜로 소통을 주고받는다. 기존 웹 드라이버는 웹 표준이며 주요 브라우저에서 모두 지원한다. 하지만 저수준의 컨트롤을 지원하지 않는다는 것이 단점이다.

/* WebdriverIO */

// Setup browser
const browser = ...;

// Action
await browser.setWindowSize(600, 1041);
await browser.url('https://coffee-cart.app');
await browser.$('[data-test="Espresso"]').click();

 

크롬 개발자 프로토콜(CDP)을 살펴보자. CDP는 가운데 드라이버 없이 크로미움 기반 브라우저에서 자동화 도구와 직접 소통한다. 자동화 도구가 명령을 CDP에 보내면 웹소켓으로 브라우저를 컨트롤할 수 있다.

/* Puppeteer */

// Setup browser
const browser = ...;
const page = await browser.newPage();

// Action
await page.setViewport({width: 600, height: 1041});
await page.goto('https://coffee-cart.app');

const coffee = await page.$('[data-test="Espresso"]');
await coffee.click();

 

웹소켓을 사용하기 때문에 긴 폴링을 기다릴 필요가 없다. 따라서 속도가 기존 웹 드라이버에 비해 빠르다. 과거에는 저수준의 컨트롤이 필요하지 않고 기존 웹 드라이버로 충분했지만, 웹이 현대화되면서 CDP와 같은 도구가 필요해졌다. 하지만 CDP도 크로미움 기반 브라우저에서만 쓸 수 있다는 한계가 분명했다. 

 

이 두 가지 도구의 장점을 가져와 만든 도구가 바로 웹 드라이버 BiDi다. 웹 드라이버 BiDi는 크로스 브라우저 자동화 프로토콜이며, 기본적으로 CDP와 동작 원리는 비슷하지만 모든 드라이버나 브라우저에서 쓸 수 있다는 특징을 갖고 있다.

/* WebdriverIO */

// Setup browser
const browser = await remote({
	capabilities: { browserName: 'chrome', webSocketUrl: true }
});

// Monitor log messages
await browser.send({
	method: 'session.subscribe',
	params: { events: ['log.entryAdded'] }
});
browser.on('message', (data) => {
	const txt = JSON.parse(data).params.text;
	assert.fail(`Unexpected console message received, ${txt}`);
});

// Action
...

 

느낀 점

브라우저 간 소통하는 자동화 도구에 대해서는 잘 몰랐는데, 이번 세션을 통해 알 수 있게 되어서 좋았다. 이번에 소개한 웹 드라이버 BiDi는 기존의 웹 드라이버와 CDP의 장점을 섞어서 만든 도구다. 앞으로 개선이 더 필요한 부분도 있지만 이렇게 각 도구의 장점을 결합하다 보면, 개발자의 생산성은 더 올라가고 웹 사용자 경험도 점점 더 나아질 것으로 기대된다.

 

 

마치며

지금까지 구글 I/O 2023의 주요 웹 세션을 살펴보았다. 이를 통해 전 세계에서 가장 많이 쓰는 검색엔진을 만든 구글이 어떤 기술적인 고민을 하고, 어떻게 업데이트를 진행했는지 알 수 있어 유익했다. 특히 안드로이드 웹뷰, 플러터, WASM 등 도구에서 많은 성능 개선이 이루어진 것을 보며 현재 어디에 집중하고 있고, 웹 생태계에서 계속 주도권을 쥐기 위해 어떤 노력을 했는지도 엿볼 수 있었다. 

 

웹 프론트엔드 개발자로서 이번 구글 I/O 웹 세션에서 다룬 새로운 기능들을 직접 써보고 싶고, 최신 버전으로 업데이트해서 변화된 성능도 체감해 보고 싶은 마음이다. 이번 글에서 다루지 못한 세션들이 많은데, 자세한 내용은 구글 I/O 2023 홈페이지를 통해 살펴볼 수 있다. 앞으로도 구글의 다양한 행보가 기대되는데, 프론트엔드 개발자로서 구글 공식 문서, 컨퍼런스를 주기적으로 팔로우하면 많은 도움을 받을 수 있을 것이다.

 

요즘IT의 모든 콘텐츠는 저작권법의 보호를 받는 바, 무단 전재와 복사, 배포 등을 금합니다.

좋아요

댓글

공유

공유

댓글 0
웹프론트엔드 엔지니어 데브오웬입니다
77
명 알림 받는 중

작가 홈

웹프론트엔드 엔지니어 데브오웬입니다
77
명 알림 받는 중
안녕하세요. 개발을 통해 비즈니스 임팩트를 내는 것에 관심이 많은 3년차 웹 프론트엔드 엔지니어 데브오웬입니다. 현재는 헬스케어 스타트업 굿닥에서 웹 프론트엔드 엔지니어로 근무하고 있으며, 병원과 환자를 위한 B2B, B2C 기반의 웹, 모바일 앱 제품을 만들고 있습니다.

동료와 함께, 때로는 혼자 세상의 다양한 문제들을 해결할 수 있는 제품을 만들고 이에 몰입할 수 있습니다. 공부하는 내용을 타인과 공유하며 서로 성장하는 것을 좋아합니다. 이를 위한 커뮤니티, 스터디, 컨퍼런스, 해커톤 등의 활동들도 다양하게 참여하고 있습니다.

블로그: devowen.com
링크드인 : linkedin.com/in/wonjongoh

좋아요

댓글

스크랩

공유

공유

요즘IT가 PICK한 뉴스레터를 매주 목요일에 만나보세요

요즘IT가 PICK한 뉴스레터를
매주 목요일에 만나보세요

뉴스레터를 구독하려면 동의가 필요합니다.
https://auth.wishket.com/login