🛑 배경: "복지인데 왜 불편하지"
우리 회사의 소중한 복지 중 하나인 중식 식대 지원. 하지만 이 혜택을 누리기 위한 과정은 생각보다 고달팠습니다.
기존 프로세스의 3중 고통
- 임시 메모의 굴레: 외부 접속이 안 되는 사내 서버 엑셀 파일 때문에, 점심 직후 카톡 '나에게 보내기'에 식당과 금액을 적어둬야 합니다.
- 복귀 후 수동 입력: 사무실에 돌아와 카톡을 뒤져가며 다시 엑셀에 옮겨 적는 이중 작업이 발생합니다.
- OS 호환성 잔혹사: macOS 사용자는 파일 저장조차 원활하지 않아 로컬로 옮겼다 서버로 다시 올리는 '파일 셔틀'을 반복해야 했습니다.
💡 해결 아이디어: "식대 전용 웹앱을 만들자"
이 비효율을 끝내기 위해 사내 서버의 엑셀 파일과 실시간으로 동기화되는 웹앱을 기획했습니다.
핵심 개발 목표
- 실시간 파일 동기화: 앱에서 입력하면 사내 서버 엑셀에 즉시 반영 (SMB 프로토콜 활용).
- 잔액 실시간 조회: 이번 달 남은 식대 잔액을 메인 화면에 표시.
- 익숙한 UX: 기존 엑셀 양식을 UI로 녹여내어 거부감 최소화.
- 접근성 개선: PWA(Progressive Web App)를 통해 모바일 앱처럼 홈 화면에 추가.
🛠️ 기술 스택: 왜 Express.js인가?
가장 중요한 기능인 사내 서버 파일 직접 제어를 위해 Node.js 환경의 Express.js를 선택했습니다.
- 성숙한 SMB 라이브러리:
@marsaud/smb2라이브러리를 통해 안정적인 네트워크 파일 접근이 가능했습니다. - Excel 처리 용이성:
xlsx (SheetJS)라이브러리와의 궁합이 좋아 메모리상에서 엑셀 데이터를 핸들링하기 적합했습니다.
🔍 핵심 기술: SMB(Server Message Block) 프로토콜
식대앱의 엔진은 SMB 프로토콜입니다. 이는 네트워크상에서 파일이나 프린터 같은 리소스를 공유하기 위한 프로토콜로, 주로 Windows 환경의 파일 공유에 사용됩니다.
구현 중 마주친 빌런들 (Trial & Error)
1. 한글 자모 분리 현상 (NFC vs NFD)
Windows 서버와 통신하다 보니 파일명의 한글이 'ㅎㅏㄴㄱㅡㄹ'처럼 분리되는 문제가 있었습니다.
- 해결:
normalize("NFC")를 사용하여 유니코드 정규화를 적용했습니다.
2. Connection Closed 에러
매번 새 연결을 맺는 것이 비효율적이라 판단해 객체를 재사용하려다 연결이 끊기는 현상이 발생했습니다.
- 해결: SMB 특성상 상태 유지가 까다로워, 안전하게 **매 요청마다 새로운 연결을 생성하고 종료(disconnect)**하는 방식으로 선회했습니다.
🚀 성능 최적화: Download 방식 vs Stream 방식
처음에는 파일을 통째로 다운로드받아 처리하려 했으나, 속도가 너무 느렸습니다.
방식 비교: 어떤 것이 더 효율적일까?
| 항목 | 로컬 다운로드 방식 | Stream 방식 (최종 채택) |
|---|---|---|
| 속도 | ❌ 느림 (전체 다운로드 대기) | ✅ 빠름 (필요한 부분만 즉시 처리) |
| I/O 부하 | ❌ 디스크 쓰기 발생 | ✅ 메모리상에서 즉시 처리 |
| 동기화 | ⚠️ 파일 덮어쓰기 로직 필요 | ✅ 실시간 버퍼 전송 |
최종 구현 코드 (Stream 기반):
// 메모리 버퍼를 활용해 SMB 서버에 직접 Read/Write 수행
async function readExcelFromSMB(fileName) {
const client = createSMBClient();
return new Promise((resolve, reject) => {
client.createReadStream(`식대/${fileName}`, (err, readStream) => {
if (err) return reject(err);
const chunks = [];
readStream.on("data", (chunk) => chunks.push(chunk));
readStream.on("end", () => {
const buffer = Buffer.concat(chunks);
const workbook = XLSX.read(buffer, { type: "buffer" });
resolve(workbook);
});
readStream.on("error", reject);
});
});
}
결과: 엑셀 조회 속도가 3초에서 0.5초로 약 6배 향상되었습니다.
⚠️ 치명적인 결함: "완벽한 줄 알았는데..."
모든 개발이 끝났다고 생각한 순간, 거대한 벽에 부딪혔습니다. 사내 와이파이를 벗어나면 앱이 작동하지 않는 것이었습니다.
- 원인: 사내 방화벽이 SMB 포트(445)를 외부로부터 철저히 막고 있었습니다.
- 팀장님의 피드백: "보안 정책상 외부 IP의 서버 접근은 절대로 허용할 수 없습니다."
외부에서도 편하게 입력하려던 앱의 근간이 흔들리는 순간이었습니다.
🏁 1편 결론: 아키텍처 재설계의 서막
사내망 안에서만 돌아가는 앱은 반쪽짜리에 불과했습니다.
저는 여기서 포기하지 않고, 사내 서버의 보안을 지키면서도 외부에서 접속 가능한 아키텍처를 고민하기 시작했습니다.
다음 편 예고:
- AWS Lightsail을 활용한 중간 서버(Proxy) 구축
- SMB를 포기하고 선택한 하이브리드 데이터 동기화 전략
- 위기를 기회로 바꾼 아키텍처 전면 재설계 과정!