[Node.js/Express.js] Session, Interceptor 기능 개발
입사 하자마자 진행했던 프로젝트인 paperless 회진 앱
의 보안 점검 결과로 여러 문제점을 지적 당했다.
크게 앱 단계의 문제와 서버 단계의 문제로 나눌 수 있었는데, 서버쪽에
- 세션 미적용
- SQL Injection
문제가 있었다.
이중 세션은 사실 JS의 Map
변수를 이용해 로그인 하는 시점에 생성, setTimeout()
을 통해 30분 후 해당 세션 삭제와 매 요청시 setTimeout()
의 key값으로 clearTimeout()
을 호출하고, 다시 30분 시작을 하도록 구현했었다.
테스트시에는 문제가 없었지만, 실제 서버에 올려보니 30분이 되지 않았는데 세션이 삭제되는 등 많은 문제가 있었다.
개발 당시에는 내가 굉장히 무지(?)했기 때문에 여러 상황을 고려하지 못했는데 지금 생각해보면 당연히 동시성 문제가 나도록 개발을 했다.
대충 코드를 보면
const sessionUtil = {
map: new Map(),
isValid(key) {
return this.map.has(key)
},
reset(key) {
const newVar = this.map.get(key);
clearTimeout(newVar.timeout);
newVar.timeout = setTimeout(() => this.map.delete(key), 30000);
},
login(key) {
const newVar = {};
newVar.timeout = setTimeout(() => this.map.delete(key), 30000);
this.map.set(key, newVar);
}
}
이런식으로 만들어두고 요청이 들어오면 isValid()
를 통해 로그인 여부 체크와 reset()
을 통해 만료시간을 초기화하도록 구성했다.
개발은 내가했지만, 서버 운영은 회사 선임개발자분이 관리하셨기 때문에 나도 모르는 사이 해당 기능을 다 막아두셨더라. 그래서 보안점검에 걸리게 되었다.
사실 이런 기능들은 프레임워크에서 당연히 지원해야 한다고 생각해왔는데 Express.js로 만들어진 사내 프레임워크는 이런 부분이 없어 아쉬웠었다.
어쨌든 이번 기회에 세션 기능을 보완하기로 했고 추가적으로 지금까진 Controller
의 모든 함수 시작부분에 일일이 isValid()
와 reset()
을 호출해뒀는데 인터셉터도 개발해 앞으로는 이런 수고를 덜게끔 기능 개발을 했다.
설계
시작하기 앞서 일단 나는 JavaScript
, Express.js
를 잘 알지 못한다. 그리고 주로 Spring Framework
를 하다보니 구현하려는 기능 또한 Spring
과 유사한 점이 매우 많다.
개인적으로 서버는 신뢰성이 중요하고 개발자의 의도대로 프로그램이 작동되도록 해야한다고 생각한다. 그래서 interface를 이용하여 신뢰성을 높이고 다형성을 이용하는 등 객체지향적 개발을 하려했지만, 아쉽게도 사내 프레임워크가 JavaScript
100%로 개발되어있었고 Node 표준 규칙(CommonJS
)을 지키고 있었기 때문에 설계에 한계가 있었다.
세션
세션이라는 객체는 인스턴스 변수로 Map
을 통해 key-value
로 이용자별 저장할 정보들을 담도록 하고, 이 세션을 저장소에 저장하는 방식으로 구상했다.
이때 저장소는 기본적으로 프레임워크가 MemoryStore
와 RedisStore
이렇게 2가지 방식을 기본적으로 지원하게끔 개발했다.
설정을 따로 하지 않으면 기본값으로 MemoryStore
를 사용하게끔 하고 사용자가 Redis 설정값을 넣어두고, 설정정보를 담는 객체에 RedisStore
를 사용하는 설정을 넣으면 RedisStore
를 사용하도록 했다.
또한 interface는 아니지만 기본 SessionStore
class를 만들어 해당 class를 상속받아 개발자가 SessionStore
를 추가적으로 개발할 수 있도록 강제했다.
인터셉터
인터셉터도 기본적으로 interface로 구현하고싶었지만 상황이 불가능했기에 SessionStore
와 마찬가지로 HandlerInterceptor
class를 두고 해당 class를 상속해 개발자가 추가적인 Interceptor
를 사용할 수 있게끔 강제했다.
설정에 Interceptor
를 등록할땐 적용할 path와 제외할 path, 그리고 호출될 순서를 설정할 수 있게 개발했다.
여태 한 개발은 단순히 이용자
가 '실제 애플리케이션을 이용하는 이용자' 였는데 이번 개발에서는 '실제 애플리케이션을 이용하는 이용자'와 '이 프레임워크를 통해 개발하는 개발자' 까지 포함되다 보니 조금 더 고려할 사항이 많았다.
하지만 정말 오랜만에 계획에서부터 신나는 느낌을 받아 너무 재밌게 개발을 했다.