기존 Mac 전용 SpriteKit 사무실을 브라우저와 Windows로 확장하되, 배치 규칙은 한곳에 두고 실행 환경에 따라 렌더링만 나눈 설계를 다뤄요.
문제: 사무실 화면이 Mac에 묶여 있었다
기존 사무실 화면은 SpriteKit으로 만든 Mac 전용 화면이었어요. 렌더링과 배치 규칙이 수천 줄에 이르는 scene-renderer.swift와 floor-plan.swift에 들어 있어 다른 운영체제에서는 그대로 실행할 수 없었어요.
가장 먼저 떠오르는 방법은 원격 데스크톱이나 화면 스트리밍이에요. 하지만 이 방식을 쓰면 Mac이 계속 화면을 렌더링하면서 픽셀 데이터를 보내야 해요. 여러 기기에서 서로 다른 크기로 화면을 열어도 결국 하나의 Mac 화면을 공유하는 셈이에요.
배치 규칙을 JavaScript로 다시 작성하는 방법도 위험했어요. 기존 배치 코드에는 좌석과 가구의 좌표만 들어 있는 게 아니었거든요. “문 앞에는 서지 않는다”, “이름표는 이웃과 중간선까지만 사용한다”처럼 운영하면서 쌓인 판단이 상수와 계산식에 녹아 있었어요.
같은 규칙을 Swift와 JavaScript에 따로 두면 어느 한쪽만 수정하는 순간 두 화면이 서로 다른 사무실을 보여줘요. 더 큰 문제는 이런 차이가 눈에 잘 띄지 않은 채 생긴다는 점이에요.
화면이 아니라 배치 데이터를 보낸다
해결책은 렌더링 책임을 분리하는 것이었어요.
Mac은 기존 배치 계산 모듈로 사무실 배치를 계산하고, 브라우저는 그 결과를 받아 자기 화면에 직접 그려요. 네트워크로 보내는 것은 완성된 화면이 아니라 다음과 같은 장면 정보예요.
- 방과 좌석의 위치
- 직원과 가구의 좌표
- 현재 업무 상태
- 렌더링에 필요한 장면 속성
배치 규칙의 원본은 계속 Mac에만 둬요. 웹 렌더러는 좌석 위치를 다시 계산하지 않고, 전달받은 좌표를 화면의 픽셀 좌표로 바꾸는 역할만 맡아요.
이렇게 경계를 나눈 덕분에 기존 SpriteKit 구현과 브라우저 구현은 서로 다른 그래픽 기술을 쓰면서도 같은 사무실을 표현할 수 있었어요. 좌석 규칙을 두 플랫폼에 중복해서 구현하지 않았으므로 나중에 배치가 바뀌어도 한 곳만 수정하면 돼요.
Mac
└─ 기존 배치 규칙으로 장면 계산
└─ 좌표와 상태 데이터 전달
├─ SpriteKit이 Mac 화면 렌더링
└─ 브라우저가 자기 화면 렌더링
화면 스트리밍을 데이터 기반 렌더링으로 바꾸면서 클라이언트는 단순한 화면 수신기가 아니라 독립적인 렌더러가 됐어요. Mac 화면을 점유하지 않고 여러 클라이언트가 각자의 해상도와 창 크기에 맞춰 같은 장면을 그릴 수 있어요.
확대보다 도면의 형태를 바꾼다
실행 환경을 웹과 Windows로 넓히려면 화면 크기뿐 아니라 창의 종횡비도 고려해야 했어요.
기존 사무실 도면은 35칸 × 20줄로 이루어진 가로형 구조였어요. 하지만 실제 콘솔 창은 960×1050처럼 세로로 긴 경우도 있었어요. 타일 한 칸의 크기는 다음과 같이 정해요.
tileSize = min(창 너비 / 도면 열 수, 창 높이 / 도면 행 수)
960×1010 창에 35×20 도면을 표시하면 가로축이 병목이 돼요.
| 계산 항목 | 값 |
|---|---|
| 가로 기준 | 960 ÷ 35 = 27.4px |
| 세로 기준 | 1010 ÷ 20 = 50.5px |
| 실제 타일 크기 | 27.4px |
배율을 가로에 맞추면 세로 공간의 약 46%가 여백으로 남아요. 사무실 자체가 작아서가 아니라 창과 도면의 형태가 맞지 않았던 거예요.
이 문제를 해결하기 위해 여러 부서 방의 배치를 창 비율에 맞춰 전환했어요.
- 가로가 넓은 창: 3열 × 2행
- 세로가 긴 창: 2열 × 3행
세로형으로 배치하면 전체 도면은 23칸 × 27줄이 돼요. 방 안의 좌석과 가구는 방 원점을 기준으로 한 상대좌표를 사용하므로 내부 배치표를 수정할 필요가 없었어요. 방의 원점만 새 격자에 놓으면 기존 좌석 구성을 그대로 유지할 수 있었어요.
단순히 화면만 확대한 것은 아니에요. 제한된 화면에 더 큰 타일을 배치할 수 있도록 도면 자체의 종횡비를 창에 맞춘 거예요. 같은 배치 데이터를 받더라도 각 클라이언트가 자기 창 비율에 맞는 형태로 사무실을 구성할 수 있게 됐어요.
webSecurity를 끄지 않고 Windows 앱을 패키징한다
브라우저 렌더러를 완성한 뒤에도 사용 절차가 문제로 남았어요. Windows PC에서 사무실을 보려면 저장소와 Python 실행 환경을 준비한 뒤, 아래와 같이 서버를 직접 띄우고 주소를 입력해야 했어요.
python3 local-server.py 8777
기능은 제대로 동작했지만 설치형 애플리케이션의 사용 경험과는 거리가 멀었어요. 그래서 Electron으로 감싸 Windows 설치 앱으로 제공했어요.
이 과정에서는 CORS가 문제가 됐어요. Electron 창에서 Mac 백엔드로 직접 요청하면 브라우저와 마찬가지로 출처 제약을 받아요. 백엔드가 Access-Control-Allow-Origin을 제공하지 않았기 때문이에요.
Electron에서는 다음과 같이 보호 기능을 끌 수도 있어요.
webSecurity: false
하지만 이 설정은 특정 요청만 허용하는 게 아니라 창 전체의 웹 보안 경계를 낮춰요. CORS 문제 하나를 피하려고 애플리케이션 전체의 보호 기능을 해제할 수는 없었어요.
대신 Electron 앱 안에 작은 서버를 띄우고, 창이 그 서버의 주소에서 렌더러를 열도록 구성했어요. 출처 문제는 앱 내부 서버의 경계에서 처리하면서 Electron 창의 webSecurity는 그대로 유지했어요.
이 구조에는 다른 장점도 있었어요. 브라우저 단계에서 Mac 앱과 대조해 검증한 렌더러 코드를 수정하지 않고 Windows 앱에 그대로 넣을 수 있었거든요. 브라우저와 Electron이 서로 다른 렌더러를 쓰지 않으므로 화면 구현의 분기도 늘어나지 않았어요.
하나의 배치, 여러 렌더러
이번 확장의 핵심은 SpriteKit 코드를 다른 플랫폼으로 이식하는 데 있지 않았어요. 기존 Mac 구현을 배치 계산의 단일 원본으로 유지하면서 렌더링 경계를 분리하는 것이 핵심이었어요.
그 결과 구조는 다음과 같이 정리됐어요.
- Mac은 기존 규칙으로 사무실 배치를 계산한다.
- 브라우저와 Windows 앱은 좌표 및 상태 데이터만 받는다.
- 각 클라이언트는 자신의 화면 크기에 맞춰 장면을 직접 그린다.
- 창 비율에 따라 3×2 또는 2×3 방 배치를 선택한다.
- Windows 앱은
webSecurity를 비활성화하지 않고 CORS 제약을 처리한다. - Windows 사용자는 Python 환경과 저장소를 준비해 웹 서버를 수동 실행하지 않아도 설치 앱을 열 수 있다.
수천 줄 규모의 Mac 전용 화면을 확장하면서도 배치 규칙은 복제하지 않았어요. 픽셀 대신 의미 있는 데이터를 보내고, 각 실행 환경이 직접 렌더링하도록 책임을 나눈 것이 핵심이었어요.
이 설계의 의미는 지원 플랫폼을 늘린 데서 그치지 않아요. 기존 도메인 로직은 한곳에 유지하면서 표현 계층만 바꿀 수 있는 경계를 만들었어요. SpriteKit, 브라우저, Electron처럼 서로 다른 실행 환경에서도 같은 사무실을 보여줄 수 있었던 이유가 바로 이 경계에 있어요.