잘못된 이해가 만든 문제와 명확한 의사소통의 중요성
들어가며
최근 한 6년차 개발자로부터 꽤 당황스러운 이야기를 들은 적이 있다. 단순히 의견 차이라고 보기에는 기본 개념 자체가 뒤섞여 있는 상태였고, 그 상태로 여러 주장을 이어가고 있었다.
- Node.js는 서버이고, React도 Node.js이기 때문에 Socket 서버를 열 수 있다.
- 브라우저에서 동작하는 JavaScript도 Node.js이므로 Node.js에서 사용하는 모듈(예: Redis, MongoDB 등)을 그대로 사용할 수 있다.
- React로 개발한 웹 애플리케이션은 S3로 배포하면 안 되고, 반드시 별도의 서버 애플리케이션을 통해 배포해야 한다.
- React도 S3에 직접 로깅을 해야 한다.
이 이야기들을 듣고 말문이 막혀버렸다. 누군가는 '회사에서 뭘 굳이 반박까지 해가며 일을 키우냐'라고 할 수 있다. 하지만, 한 사람의 말도 안 되는 논리로 인해 팀 전체의 분위기를 무너뜨리고, 잘못된 개념을 주입시킬 수 있다는 위기감이 들어 반박을 통해 바로 잡을 수 밖에 없었다.
게다가 신입으로 들어오신 분들께도 저런 말을 하고 있는 것을 들었기 때문에 도무지 못본체 할 수가 없었다.
그러나, 질문을 던져보며 반박을 시도해보았으나 마치 사이비 종교나 공상 과학에 빠진 사람과 대화하는 느낌이었다. 그리고 필자에게 아래와 같은 말을 통해 가스라이팅 시도만 반복되었다.
'원래 잘 모르는 경우에는 경력 많은 사람의 말이 이해가 안 되고 화만 내더라'
'자기도 그랬다. 찾아봐라'
이 말들을 듣고, 혹시 내가 다른 세상에서 살다가 왔나 싶을 정도였다. 6년차 개발자의 말이 맞다면 그동안 내가 아는 JavaScript와 Node.js의 개념이 완전히 잘못된 것이기 때문이었다.
하지만, 그럴 일은 없다는 것은 이미 너무도 잘 알고 있었다
더이상 대화를 이어가기보다는 서로가 알고 있는 JavaScript와 Node.js의 개념부터 일치시켜야 이 대화가 의미가 있겠다라고 판단하여 나중에 다시 대화하자고 했다. 지금도 이 말을 떠올리면 당황스러움과 함께 답답함이 먼저 느껴진다. 이 글은 단순한 분풀이가 아니라, JavaScript와 Node.js의 개념이 왜 정확해야 하는지, 그리고 이러한 개념이 어긋났을 때 어떤 오해가 발생하는지를 정리하기 위해 작성했다.
1. 개요
1.1. JavaScript
JavaScript의 역사와 등장 배경은 별도의 글에서 다루었기 때문에 여기서는 핵심만 정리한다.
JavaScript는 특정 플랫폼에 종속된 언어가 아니라, ECMAScript라는 표준을 기반으로 하는 프로그래밍 언어이다. 다만 우리가 일반적으로 접하는 JavaScript는 웹 브라우저 환경에서 실행되는 경우가 많기 때문에, HTML을 조작하는 스크립트 언어로 오해되는 경우가 많다.
웹 브라우저에서 실행되는 JavaScript는 브라우저에 내장된 엔진(V8, SpiderMonkey 등)에 의해 실행되며, DOM을 조작하거나 사용자 인터랙션을 처리하는 역할을 수행한다. 이 환경에서는 보안상의 이유로 파일 시스템, OS, 네트워크 소켓 등에 직접 접근할 수 없다.
즉, 브라우저 환경에서의 JavaScript는 웹 문서를 다루기 위한 언어라기보다, 브라우저가 제공하는 API를 기반으로 동작하는 실행 환경 위의 언어라고 보는 것이 더 정확하다.
1.2. Node.js
Node.js는 Chrome의 V8 엔진을 기반으로 만들어진 JavaScript 런타임(RunTime, 실행환경)이다.즉, 브라우저가 아닌 환경에서도 JavaScript를 실행할 수 있도록 만든 것이며, 파일 시스템 접근, 네트워크 통신, 프로세스 제어 등 서버 애플리케이션 개발에 필요한 기능을 제공한다. 정리하자면 JavaScript라는 언어 자체가 달라지는 것이 아니라, 실행 환경이 브라우저냐 Node.js냐에 따라 사용할 수 있는 기능이 달라지는 것이라고 할 수 있다.
과거에는 JavaScript가 브라우저 안에서만 동작하는 언어였지만, Node.js의 등장 이후 서버 사이드에서도 동일한 언어를 사용할 수 있게 되었고, 이로 인해 JavaScript의 활용 범위가 크게 확장되었다.
1.3. 개념 정리
결국 핵심은 하나다. JavaScript는 언어이고, Node.js는 그 언어를 실행하는 환경이다. 이걸 명확하게 이해하지 않으면, 'Node.js는 서버다', 'React도 Node.js다' 같은 말이 자연스럽게 나오게 된다. 브라우저에서 실행되는 JavaScript와 Node.js에서 실행되는 JavaScript는 같은 언어지만 실행 환경이 다르기 때문에 사용할 수 있는 API와 기능이 완전히 다르다.
/** browser */
console.log(this); // Window
/** node.js */
console.log(this); // {}이 차이는 단순한 문법 차이가 아니라, 어디에서 실행되느냐에 따른 환경 차이를 보여주는 예시다.
- 브라우저: DOM 접근 가능, 파일 시스템 접근 불가
- Node.js: 파일 시스템 접근 가능, DOM 접근 불가
이처럼 두 환경은 서로 겹치는 부분도 있지만, 기본적으로는 서로 다른 역할을 가진 실행 환경이다.
2. 궤변 정리
2.1. Node.js는 서버이고, React도 Node.js이기 때문에 Socket 서버를 열 수 있다?
이 문장을 처음 들었을 때는 얼핏 그럴듯하게 들릴 수도 있다. 'Node.js는 서버니까, JavaScript로 작성된 React도 결국 서버에서 돌아가는 것 아닌가?'라는 식으로 생각하면 자연스럽게 이어질 수 있기 때문이다. 하지만 이 문장은 사실 두 가지 개념이 뒤섞이면서 완전히 다른 결론으로 이어진 경우다.
먼저 Node.js를 어떻게 이해하고 있는지부터 짚어볼 필요가 있다. 많은 사람들이 Node.js를 '서버'라고 표현하는데, 정확하게 말하면 Node.js는 서버가 아니라 JavaScript를 실행할 수 있는 하나의 런타임 환경이다. 이 환경 위에서 Express 같은 프레임워크를 사용하면 HTTP 서버를 만들 수 있기 때문에, 결과적으로 서버를 만들 수 있는 도구로 사용되는 것이다.
즉, Node.js 자체가 서버라기보다는 서버를 포함해 다양한 프로그램을 실행할 수 있는 실행 환경에 가깝다. 실제로 Node.js는 웹 서버뿐만 아니라 CLI 도구, 빌드 스크립트, 배치 프로그램 등 다양한 형태로 사용된다. 이 단계까지만 보면, 'Node.js는 서버다'라는 표현이 얼마나 부정확한지 알 수 있다.
이 상태에서 다음 문장을 보면 문제가 더 명확해진다.
React도 Node.js이기 때문에 Socket 서버를 열 수 있다?
이 문장은 React가 무엇인지에 대한 이해가 잘못된 상태에서 나온 결론이다. React는 서버 환경에서 동작하는 것이 아니라 기본적으로 브라우저에서 실행되는 JavaScript 코드, 더 정확하게는 UI를 구성하기 위한 라이브러리다. 즉, React 코드가 실행되는 환경은 Node.js가 아니라 브라우저다.
여기서 중요한 차이는 '언어가 무엇이냐?'가 아니라, '어디에서 실행되느냐'이다. JavaScript라는 동일한 언어를 사용하더라도, 브라우저에서 실행되는지, Node.js에서 실행되는지에 따라 할 수 있는 일이 완전히 달라진다. Socket 서버를 연다는 것은 단순히 함수를 호출하는 수준의 작업이 아니다. 서버를 연다는 것은 운영체제 수준에서 포트를 열고, 해당 포트로 들어오는 네트워크 요청을 처리하는 프로그램을 실행한다는 의미다. 즉, 다음과 같은 작업이 필요하다.
- 특정 포트에 바인딩(bind)
- TCP 소켓 생성 및 리스닝
- 지속적으로 요청을 처리하는 서버 프로세스 유지
이 작업들은 모두 운영체제 자원에 접근해야 하는 영역이다. 그런데 브라우저 환경에서는 보안상의 이유로 이런 작업이 원천적으로 차단되어 있다. 브라우저는 사용자 환경에서 동작하는 프로그램이기 때문에 임의로 포트를 열거나 네트워크 서버를 실행하는 것이 허용되지 않는다. 결국 React 코드로는 Socket 클라이언트는 만들 수 있어도, Socket 서버를 여는 것은 불가능하다. 이건 라이브러리의 문제가 아니라 실행 환경의 제약 때문이다.
이런 오해가 발생하는 이유는 보통 하나다.
- Node.js에서 React를 실행할 수 있다는 사실
- 'React 자체가 Node.js다'라는 잘못된 해석
실제로 React 개발 과정에서는 Node.js를 사용한다. 예를 들어 npm start, npm run build 같은 명령은 Node.js 환경에서 실행된다. 하지만 이건 어디까지나 개발 도구로서 Node.js를 사용하는 것이지 최종적으로 실행되는 React 코드가 Node.js 위에서 돌아간다는 의미는 아니다.
정리하자면 React는 브라우저에서 실행되는 UI 라이브러리이고, Node.js는 서버를 포함한 다양한 프로그램을 실행할 수 있는 런타임 환경이다. 이 둘을 동일한 것으로 보는 순간, 'React로 서버를 열 수 있다'는 잘못된 결론으로 이어지게 된다. 둘은 같은 JavaScript를 사용한다는 공통점은 있지만, 역할도 다르고, 실행 환경도 완전히 다른 존재다.
2.2. 브라우저에서 동작하는 JavaScript도 Node.js이므로 Node.js 모듈을 그대로 사용할 수 있다?
이 주장은 앞선 내용보다 더 명확하게 잘못된 이해에서 나온 말이다. 겉으로 보면 '같은 JavaScript인데 왜 안 되지?'라고 생각할 수 있지만, 이건 언어와 실행 환경을 구분하지 못했을 때 흔히 나오는 오해다.
먼저 가장 기본적인 전제부터 다시 짚어볼 필요가 있다. JavaScript는 하나의 프로그래밍 언어이고 Node.js는 그 언어를 실행하기 위한 환경이다. 그리고 브라우저 역시 JavaScript를 실행할 수 있는 또 하나의 환경이다. 즉, JavaScript는 동일하지만 어디에서 실행되느냐에 따라 전혀 다른 제약과 기능을 가지게 된다.
이 차이를 이해하지 못하면 'Node.js에서 되니까 브라우저에서도 되겠지'라는 식의 결론으로 이어지게 된다. 브라우저 환경에서는 보안상의 이유로 매우 강력한 제약이 걸려 있다. 사용자의 컴퓨터에서 실행되는 코드이기 때문에 시스템에 영향을 줄 수 있는 기능은 기본적으로 차단되어 있다. 예를 들어, 다음과 같은 작업들은 브라우저에서는 수행할 수 없다.
- 로컬 파일 시스템에 직접 접근
- 운영체제 레벨의 자원 제어
- TCP 소켓을 직접 생성하거나 제어
이러한 기능들은 Node.js에서는 기본적으로 제공되지만 브라우저에서는 의도적으로 막혀 있는 영역이다. 그래서 Node.js에서 사용하는 대표적인 모듈들인 fs, net, tls, dns 같은 것들은 브라우저 환경에서는 사용할 수 없다. 단순히 지원이 안 된다는 문제가 아니라 애초에 브라우저가 이런 기능을 제공하지 않기 때문이다.
이와 같은 이유로 Redis나 MongoDB 같은 라이브러리도 브라우저에서는 직접 사용할 수 없다. 이 라이브러리들은 내부적으로 TCP 연결이나 시스템 자원을 사용하기 때문에 Node.js 환경에서는 정상적으로 동작하지만 브라우저에서는 실행 자체가 불가능하다.
이 부분에서 중요한 것은 'JavaScript니까 된다'가 아니라, '어디에서 실행되느냐에 따라 가능한 일이 달라진다'는 점이다. 같은 JavaScript 코드라고 해도 브라우저에서는 웹 페이지를 다루는 코드로 동작하고, Node.js에서는 시스템과 네트워크를 다루는 코드로 동작한다. 이 둘은 겉보기에는 비슷해 보여도 실제로는 전혀 다른 환경 위에서 실행되는 코드다.
결국 이 주장의 문제는 단순하다. 언어만 보고 동일하게 취급해버리면서 실행 환경의 차이를 완벽하게 무시한 것이다. 이 차이를 이해하지 못하면 어떤 라이브러리는 되고 어떤 라이브러리는 안 되는지조차 설명할 수 없게 된다.
2.3. React는 S3로 배포하면 안 되고 반드시 서버로 배포해야 한다?
이 주장 역시 특정 상황을 전체 상황으로 일반화한 경우에 가깝다. React로 만든 웹 애플리케이션이 어떤 방식으로 배포되어야 하는지는 React 자체의 문제가 아니라 해당 애플리케이션이 어떤 방식으로 동작하도록 만들어졌는지에 따라 달라진다.
일반적인 React SPA를 기준으로 보면 빌드 결과물은 결국 정적 파일이다. npm run build를 실행하면 HTML, CSS, JavaScript 파일이 생성되고, 브라우저는 이 파일들을 내려받아 실행한다. 즉, 서버에서 React 코드가 계속 실행되는 것이 아니라 서버는 빌드된 정적 파일을 전달하고, 실제 애플리케이션 동작은 사용자의 브라우저에서 이루어진다.
이 구조에서는 반드시 별도의 서버 애플리케이션이 필요하지 않다. S3 같은 정적 파일 저장소에 빌드 결과물을 올리고, CloudFront 같은 CDN을 붙이면 충분히 배포가 가능하다. Netlify나 Vercel 같은 플랫폼을 사용하는 것도 같은 맥락이다. 결국 React SPA 배포에서 중요한 것은 '정적 파일을 안정적으로 제공할 수 있는가?'이지 '반드시 Node.js 서버가 있어야 하는가'가 아니다.
물론 서버가 필요한 경우도 있다. 예를 들어 Next.js처럼 SSR(Server Side Rendering)을 사용하는 경우에는 요청 시점에 서버에서 HTML을 생성해야 하므로 서버 실행 환경이 필요할 수 있다. API 프록시가 필요하거나, 서버에서 인증 관련 처리를 해야 하는 경우에도 별도의 서버 애플리케이션을 둘 수 있다. 하지만 이것은 어디까지나 특정 요구사항이 있을 때의 이야기다.
따라서 'React는 S3로 배포하면 안 된다'거나 '반드시 서버로 배포해야 한다'는 말은 정확하지 않다. 더 정확하게 말하면 일반적인 React SPA는 S3와 CDN 같은 정적 호스팅 환경에 배포할 수 있고, SSR이나 서버 사이드 처리가 필요한 경우에만 별도의 서버 환경을 고려하면 된다.
2.4. React도 S3에 직접 로깅을 해야 한다?
이 주장 역시 단순한 구현 선택의 문제가 아니라 구조적으로 잘못된 이해에서 나온 이야기다. 특히 이 경우는 단순히 '비효율적이다'를 넘어서, 실제 서비스 환경에서는 보안적으로도 문제가 될 수 있는 접근이다.
브라우저에서 S3에 직접 로그를 쌓는다는 것은 결국 클라이언트가 S3에 직접 요청을 보내서 데이터를 저장한다는 의미다. 그런데 이 과정에서 반드시 고려해야 할 부분이 있다. S3는 기본적으로 인증된 요청만 허용하는 서비스이기 때문에 클라이언트가 직접 접근하려면 인증 정보가 필요하다.
이때 선택지는 크게 두 가지로 나뉜다. 하나는 Access Key와 같은 인증 정보를 클라이언트에 포함시키는 것이고, 다른 하나는 S3 버킷을 Public으로 열어두는 것이다. 하지만 이 두 방식 모두 문제가 있다.
Access Key를 클라이언트에 포함시키는 것은 곧 인증 정보를 외부에 노출하는 것과 같다. 이는 보안적으로 매우 위험한 접근이다. 반대로 Public 권한을 열어두는 방식 역시 누구나 해당 버킷에 접근할 수 있게 되는 구조이기 때문에 의도하지 않은 데이터가 쌓이거나 악용될 가능성이 존재한다.
또한 S3의 역할 자체를 생각해볼 필요도 있다. S3는 로그를 수집하고 분석하는 시스템이 아니라, 단순히 파일을 저장하는 Object Storage다. 클라이언트에서 로그를 보낼 때마다 객체가 하나씩 생성되는 구조라면 요청 수에 비례해서 객체 수가 증가하게 되고 이는 관리 측면이나 비용 측면에서도 비효율적이다.
굳이 S3에 로그를 수집해야한다면 실제 서비스에서는 다음과 같은 방식으로 처리하는 경우가 일반적일 것이다.
- 먼저 클라이언트에서 발생한 로그를 서버로 전달
- 서버에서 이를 적절한 형태로 가공한 뒤 저장하거나, 필요하다면 일정 단위로 묶어서 S3에 적재
일반적으로는 Sentry와 같은 외부 로깅/모니터링 서비스를 활용하여 클라이언트에서 직접 이벤트를 전송하도록 구성하며, 심지어 회사에서 개발한 웹 서비스도 Sentry로 로그를 쌓고 있다.
이렇게 보면 '클라이언트에서 S3로 직접 로깅한다'는 구조는 일반적으로 사용되는 방식도 아니고, 굳이 선택할 이유도 없는 구조다. 단순히 가능하냐의 문제가 아니라 실제 서비스 환경에서 안전하고 효율적인 방식이 무엇인지를 따져볼 필요가 있다.
3. 에필로그
다음 날 출근을 했는데 그 6년차 개발자가 커피챗을 요청해왔다. 내 블로그 글을 읽어봤다고 했다. 그리고 이어서 이런 말을 했다.
자기가 말한 React는 Next.js를 의미한 것이다.
요즘 React라고 하면 어딜가나 전부 Next.js를 의미한다.
이 말을 듣고 즉시 아래와 같이 답을 했다.
React는 라이브러리이고, Next.js는 프레임워크이다. 엄연히 다르다.
혼동해서 부르면 안 된다.
이어서 그 개발자는 또 다른 궤변을 늘어놓았다.
React도 프레임워크다.
더 이상 할 말이 없었다. 빠르게 대화를 마무리짓고, 사무실로 올라가서 프론트엔드 개발자까지 모두 모아놓고 React 공식 사이트에 들어가서 확인시켜주었다.
Next.js는 React를 기반으로 만들어진 프레임워크이지 React 그 자체를 대체하는 개념은 아니다. 둘은 분명히 구분되는 개념이고 그 차이를 정확하게 알고 사용하는 것이 중요하다. 물론 실무에서는 특정 기술을 지칭할 때 약간의 생략이나 관용적인 표현이 사용될 수는 있다. 하지만 그 표현이 개념 자체를 흐릴 정도라면 이야기가 달라진다. 특히 누군가에게 설명을 해야 하는 상황이라면 더더욱 용어를 정확하게 사용하는 것이 필요하다.
이 경험을 통해 다시 한 번 느낀 점은 단순히 기술을 많이 아는 것과, 그 기술을 정확하게 이해하고 설명할 수 있는 것은 전혀 다른 문제라는 것이다. 그리고 그 차이는 결국 기본 개념을 얼마나 정확하게 알고 있는지에서 갈린다고 생각한다.
어쩌면 이번 일에서 가장 중요했던 부분은 기술적인 논쟁이 아니라, 서로 같은 단어를 쓰고 있지만 전혀 다른 개념을 떠올리고 있었다는 점이 아닐까 싶다. 결국 명확한 의사소통은 정확한 개념 위에서만 가능하다는 것을 다시 한 번 깨닫는다.
마치며
이 글을 쓰게 된 이유는 단순히 잘못된 말을 들었기 때문만은 아니었다. 그보다 더 크게 와닿았던 부분은 그 내용이 주니어 개발자에게까지 전달되고 있었다는 점이었다.
연차가 조금 더 높다고 해서 항상 올바른 정보를 가지고 있는 것은 아니다. 오히려 경험에 기반한 잘못된 확신은 더 단단하게 굳어지기 쉽고, 그만큼 주변 사람들에게도 더 강하게 영향을 줄 수 있다. 그래서 연차가 쌓일수록 새로운 것을 배우는 것만큼이나 자신이 알고 있는 내용을 다시 검증하는 과정이 중요하다는 생각이 들었다. 결국 스스로에게 계속 질문해야 한다고 생각한다.
- 내가 알고 있는 개념을 정말 정확하게 이해하고 있는가?
- 그리고 지금 알고 있는 내용이 과연 맞는 것인가?
이 질문을 놓치기 시작하는 순간, 그때부터는 더 이상 성장하기 어려워진다고 느낀다. 이번 일을 겪으면서 한 가지 더 분명해진 것도 있다. 옳고 그름을 명확하게 판단하기 위해서라도 결국은 스스로 계속 공부할 수밖에 없다는 점이다. 누군가의 말이나 경력에 의존하기보다는 직접 확인하고 이해하려는 과정이 쌓여야만 제대로 된 판단을 할 수 있다고 생각한다. 그래서 이 글은 단순한 경험 공유를 넘어서 스스로에게도 다시 한 번 되짚어보는 계기가 되었다.