포스를 확장하는 가장 빠른 방법, 포스 플러그인

고태완/윤보미
2025년 10월 29일

안녕하세요, 토스플레이스 Frontend Developer 고태완, Node.js Developer 윤보미입니다.

“여러분은 오프라인 매장에서 주문하고 결제하는 과정을 얼마나 자주 경험하시나요?” 카페에 들어가 메뉴를 주문하고, 점주분이 포스에 상품을 입력하고, 결제 버튼을 누르면 카드가 읽히고 결제가 완료되는, 우리 모두에게 익숙한 이 과정. 토스플레이스는 이런 오프라인 매장의 일상을 다루고 있습니다.

토스 포스와 토스 프론트

포스에 필요한 기능, 끝이 없었습니다

개발자분들이 IDE를 매일 사용하듯이, 점주분들은 포스를 매일 사용합니다. 매일 사용하는 만큼 포스를 선택할 때는 기능 하나하나가 중요합니다. 기존 포스에 있던 기능이 없으면 포스를 바꾸는 데 큰 허들이 되기 때문이에요. 포스의 기본적인 기능은 명확합니다.

하지만 기본 기능만으로는 부족했어요. 그래서 토스플레이스는 오프라인에 필요한 더 많은 기능을 계속해서 만들어왔습니다.

그럼 이제 모든 준비가 끝난 걸까요?

아니었습니다. 마트는 재고 관리와 수발주가 필요했고, 병원과 약국은 환자 정보 및 처방전 연동이, 헬스장과 학원은 회원권 관리와 출입 인증이 필요했어요. 토스플레이스가 더 많은 매장에 선택받기 위해서는, 이 모든 기능을 갖춰야 했습니다. 이를 전부 직접 만든다면 얼마나 많은 시간과 비용이 들까요?

제휴 문의가 답을 주었습니다

이때 제휴 문의가 들어오기 시작했습니다.

"토스 포스의 API를 사용할 수 있나요?"

이런 회사들은 이미 매장들과 연결되어 있었고, 여러 업종에 특화된 기능도 가지고 있었어요. 함께 하면 포스 확산이 더 빠르겠다고 생각했습니다. 하지만 제휴가 많아질수록 문제점들이 보이기 시작했어요.

1️⃣ 복잡도 증가 포스는 사장님의 매출에 직접적인 영향을 미치기 때문에 안정성이 매우 중요한데, 특정 회사에 종속적인 코드가 늘어날수록 제품의 복잡도가 높아졌습니다. 높은 복잡도는 안정성에 부정적인 영향을 미쳤어요.

2️⃣ 유지보수의 어려움 제휴사의 문서를 전달받아 작업하면서 문서 관리와 히스토리 파악이 어려웠어요. 특히 개발을 담당했던 사람이 아닌 다른 사람이 이슈를 파악해야 할 때 더 크게 느껴졌습니다.

3️⃣ 애매한 DRI 토스팀에서 DRI(Directly Responsible Individual)는 한 팀에서 제품의 오너십을 갖고 의사 결정하는 것을 말하는데, 조직 개편이 있을 때마다 제휴 개발건들은 DRI가 모호했어요.

이러한 문제점들을 해결하기 위해 플러그인 사일로포스 플러그인이 생겼습니다.

VSCode처럼 동작하는 구조

혹시 VSCode 사용해본 적 있으신가요? 아마 다들 써보셨을 거예요. VSCode를 쓰셨던 개발자분들은 ESLint나 Prettier 같은 익스텐션을 사용해보신 적이 있을텐데요.

포스도 같은 구조로 동작할 수 있으면 좋겠다고 생각했어요. 포스의 주문이나 결제 과정에서 플러그인이 특정한 동작을 하고, 그 결과를 다시 포스에 반영하는 구조로요.

웹 워커를 활용한 안전한 격리

토스 포스는 VSCode처럼 일렉트론 위의 웹뷰 환경에서 동작하기 때문에 JavaScript로 작성한 코드를 실행할 수 있습니다. 작성된 코드는 웹 워커 위에서 실행되는데요, 안정성이 중요한 포스인 만큼 플러그인에서 장애가 발생하더라도 포스의 기본 기능에는 영향이 없도록 장애 격리를 위해 웹 워커를 사용했어요.

웹 워커는 포스와 직접 통신할 수 없기 때문에 메시지를 사용해 통신합니다. 플러그인에서 쉽게 사용할 수 있도록 SDK를 통해 코드를 추상화했어요.

// 플러그인에서의 주문 추가 예시
await posPluginSdk.order.add({
  discounts: [],
  lineItems: []
})

포스는 이 메시지를 받으면 플러그인 주문을 토스플레이스 서버에 전달합니다. 주문을 예시로 들었지만 결제, 결제수단, 테이블 등 포스에 있는 여러 리소스에 접근할 수 있습니다.

복잡도를 낮추는 추상화

기존에는 제휴건마다 N개의 코드가 추가되는 구조였다면, 이제 플러그인 주문이라는 규격을 통해 이 함수 외에는 추가하지 않아도 되는 구조가 됐습니다. 제휴사가 늘어나더라도 신규 기능이 아니라면 포스에는 코드를 추가하지 않아도 제휴할 수 있어요.

예를 들어 자사 기프티콘을 갖고 있는 프랜차이즈 회사와 제휴한다고 가정해볼게요.

토스 포스는 N개의 기능에서 M개의 제휴사만큼의 코드가 아니라 N개의 기능을 위한 한 개의 코드만 잘 관리하면 됩니다.

플러그인, 실제로 어떻게 적용하나요?

이제는 조금은 추상적일 수 있는 개념인 플러그인을 실제로 어떻게 구현하는지, 쉽게 코드로 설명해 드릴게요.

💡 상황 설정

여러분은 작은 스타트업에 합류한 개발자입니다. 이 스타트업은 결제 회사로 새로운 페이를 만들려고 해요. 대표님이 이런 미션을 줍니다.

"매장에서 우리 회사 바코드로 결제할 수 있게 만들고, 결제하면 포인트도 적립하게 만들어줘"

이 시스템을 어떻게 만들 수 있을지 상상이 되시나요?

위에 설명드린 것처럼, 토스 포스 플러그인을 사용하면 이 모든 고민이 해결됩니다.

✅ 1단계: 환경 세팅

플러그인 모듈은 JavaScript, TypeScript로 개발되어 있습니다. 프로젝트에 플러그인 모듈을 설치하면 환경 세팅이 완료됩니다.

npm install @toss-place/plugin-sdk

SDK 문서에서 제공하는 기본 템플릿을 다운받으시면 간단한 테스트 환경도 제공됩니다.

✅ 2단계: 결제수단 등록하기

먼저 바코드로 결제하려면 현금, 카드와 같이 결제수단을 추가해야 합니다.

import { posPluginSdk } from "@tossplace/pos-plugin-sdk";

// 결제수단 등록
posPluginSdk.paymentMethod.add({
  data: {
    // 결제수단 정보
    id: 'tmc25',
    name: 'TMC바코드',
    icon: '<https://example.com/icon.png>',
  },
  // 결제 콜백
  payCallback: async (params) => {
    await handlePayment(params)
  },
  // 취소 콜백
  cancelCallback: async (params) => {
    // 취소 로직
  },
});

정상적으로 등록이 완료되었다면, 사장님이 실제로 결제를 진행할 때 TMC바코드 결제수단이 POS 화면에 버튼으로 표시됩니다. 기존 카드결제나 현금결제 외, 여러분의 브랜드가 담긴 새로운 결제수단이 생기는 거죠.

✅ 3단계: 바코드로 결제하기

async function handlePayment(params) {
  // 1. 바코드 입력 화면 띄우기
  const barcode = await posPluginSdk.ui.openPopup({
    type: 'barcode'
    productName: 'TMC',
  });

  // 2. 서버에 결제 요청
  const result = await fetch('<https://api.mycompany.com/pay>', {
    method: 'POST',
    body: JSON.stringify({
      barcode,
      amount: params.amount
    })
  });

  // 3. 포인트 적립 처리
  await processPointAccumulation(result);

  // 4. 결제 결과 반환
  return {
    paymentKey: result.transactionId,
    approvedAt: result.approvedAt
  };
}

UI는 토스 포스가 제공하고, 핵심 로직은 여러분의 서버가 처리하는 구조라서 깔끔하게 역할을 나눌 수 있습니다.

이렇게 아주 손쉽게 실질적인 결제 기능 하나를 완성시켰어요. 복잡해 보이던 포스 연동, 바코드 인식, 포인트 적립 모두 플러그인 환경 덕분에 간단하게 구현할 수 있는 것이지요.

✅ 4단계: 배포

배포는 토스플레이스 개발자센터에서 진행합니다.

앞으로는 사장님이 앱스토어처럼 여러 플러그인 중 원하는 플러그인을 선택하여 포스에 설치할 수 있도록 구현될 예정입니다.

플러그인이 없었다면?

만약 플러그인이 없었다면 어떻게 개발해야 했을지, 표로 비교해 보여드릴게요.

개발자 경험을 위한 고민들

1️⃣ UI 확장성에 대한 고민

플러그인을 만드는 과정에서 많이 고민했던 부분은 "어디까지 UI에 접근하게 할 것인가" 였습니다.

  • 결제수단만 등록하면 될까요?
  • 포인트 결제 가능 상품인지 체크 옵션을 넣으려면?
  • 메뉴 선택할 때마다 칼로리를 표시하고 싶다면?

다양한 요청을 받을 때마다 "혹시 이 요구가 특정 파트너의 상황에만 지나치게 맞춰진 건 아닐까?" 하는 질문을 던졌습니다. 지금까지 개발된 UI는 정말 핵심적이고 모두에게 필요한 몇 가지에 집중되어 있어요.

2️⃣ 개발 편의성 제공

원래 플러그인과 포스는 JSON만 주고받는 이벤트 기반 비동기 통신 방식입니다. 하지만 이런 방식은 매번 메시지 타입을 구분하고 응답 타이밍을 따로 처리해야 해서 개발자 입장에서 다루기가 번거로웠습니다.

여러분께 익숙하고 통상적인 개발 경험을 드리기 위해, 내부적으로 많은 추상화 작업과 구조 설계를 했습니다. 실제 토스 포스는 방대한 시스템 위에서 돌아가지만, 플러그인은 그런 복잡함을 전면에 드러내지 않습니다.

일반적인 개발자에게 낯설거나 부담스러운 요소들은 최대한 제거하거나 익숙한 방식으로 추상화해서, 최소한의 코드, 짧은 러닝 커브만으로도 기능을 만들 수 있도록 구조화했습니다.

앞으로의 확장 계획

1️⃣ UI 확장

현재는 결제수단 등록, 테이블 주문, 바코드 화면 등 핵심 기능 위주로만 접근이 가능합니다. 하지만 앞으로는 이렇게 확장할 계획이에요.

  • HTML을 직접 넣어 페이지를 유저에게 표시
  • 각 플러그인의 온보딩 화면 제공
  • VSCode Extensions처럼 플러그인 개발자가 POS UI 흐름 속에서 필요한 위치에 자연스럽게 개입하도록 확장

2️⃣ 인터페이스 확장

현재 제공하는 인터페이스는 다음과 같아요.

  • 메뉴, 테이블
  • 웹소켓, HTTP 통신
  • 기본적인 POS 리소스 접근

이미 이 인터페이스를 통해 테이블 원격 주문 서비스, 배달사 연동 등의 실제 플러그인이 토스 포스에서 실행되고 있습니다. 앞으로는 플러그인 개발사마다 갖고 있는 다양한 니즈에 맞춰 더 세분화된 기능과 인터페이스를 유연하게 열어줄 계획이에요.

플러그인이라는 제품은 현실의 다양한 요구사항을 추상화하는 과정처럼 느껴졌습니다. 이러한 규격을 만들어둔다면, 서비스는 더 빠르고 안전하게 성장할 수 있는 기술적인 기반이 될 것이라고 생각해요. 나중에는 개인도 플러그인을 토스 포스 스토어에 등록할 수 있고, 점주분들은 내 매장에 필요한 기능을 원하는 만큼 선택해서 사용할 수 있게 하려고 합니다. 마치 앱스토어 같은 생태계로 나아가고 싶습니다. 주문과 결제 외에도 세무/회계나 근무 스케줄 등 오프라인에서 발생할 수 있는 모든 경우의 수를 플러그인으로 풀고 싶어요.

만약 여러분이 앞으로 사장님들을 위한 서비스를 만들 계획이 있으시다면, 토스 포스 플러그인을 한번 활용해 보시는 것도 정말 좋은 선택이 될 수 있을 거예요. 저희가 고민하고 다듬어온 이 환경이, 여러분의 아이디어를 더 쉽게 실현하는데 도움이 되길 바랍니다.

*토스 포스의 플러그인에 더 자세히 알아보고 싶다면, 토스플레이스 연동 가이드를 확인해보세요.

✅ 이번 아티클은 아래 Toss Makers Conference 25의 세션을 바탕으로 재구성되었습니다.

댓글 0댓글 관련 문의: toss-tech@toss.im
㈜비바리퍼블리카 Copyright © Viva Republica, Inc. All Rights Reserved.