클라이언트 측 코드 보호 및 데이터 수집의 진위성 인증

데이비드 세네칼(David Senecal)

에 의해 작성

David Sénécal

July 08, 2025

데이비드 세네칼(David Senecal)

에 의해 작성

David Sénécal

데이비드 세네칼(David Sénécal)은 Akamai에서 엔지니어링 디렉터(사기 및 악용 방지 담당)로 근무하고 있으며, The Reign of Botnets의 저자이기도 합니다. 그는 인터넷의 안전성을 개선하는 데 열정을 가지고 있으며 온라인 사기 및 봇 탐지 분야의 전문가입니다. 웹 성능, 보안, 기업 네트워킹 기술 분야에서 지원, 통합, 컨설팅, 개발, 제품 관리, 아키텍처, 리서치 등 다양한 역할을 맡으며 25년 이상의 경력을 쌓아왔습니다.

클라이언트 측 코드와 페이로드를 안전하게 보호하려면, 여러 보안 기술과 방어 레이어가 결합된 복합적인 전략이 필요합니다.
클라이언트 측 코드와 페이로드를 안전하게 보호하려면, 여러 보안 기술과 방어 레이어가 결합된 복합적인 전략이 필요합니다.

목차

웹 애플리케이션과 웹사이트의 효율적으로 보호하려면, 브라우저에서 자바스크립트를 사용해 클라이언트 측 데이터를 수집하는 것이 필수적이라는 사실은 잘 알려져 있습니다. 수집되는 데이터는 일반적으로 디바이스 및 브라우저 특성, 사용자 설정(핑거프린팅), 마우스 이동, 터치, 키보드 입력 등 사용자의 디바이스 상 행동을 반영한 데이터(텔레메트리)로 구성됩니다.

웹 보안 전문가와 벤더사는 단순 룰 기반부터 고도화된 AI 모델까지 다양한 탐지 방법을 활용해, 요청이 실제 사용자가 제어하는 정상적인 디바이스에서 발생했는지 확인합니다.

다양한 데이터 포인트를 조합하면 사용자를 구분하고 장기적인 활동을 평가하는 데도 도움이 됩니다. 이 원칙은 크리덴셜 스터핑, 계정 탈취, 계정 생성 남용, 콘텐츠 스크레이핑 등 다양한 공격을 탐지하는 데 사용되는 봇 관리 및 사기 탐지 제품의 핵심 기반이 됩니다.

데이터 수집 무결성

데이터의 진위성과 무결성을 보장하는 것은 사용자의 사이트 상호작용을 정확히 평가하고 위협을 탐지하는 데 있어 핵심적인 요소입니다. 하지만 클라이언트 측에서 실행되는 모든 코드는 언제든지 조작되거나 변조될 수 있다는 점을 고려할 때, 누가 그 데이터가 진본이며 무결하다고 자신 있게 말할 수 있을까요?

클라이언트 측 자바스크립트 코드를 실행할 때 코드를 철저히 보호해야 하는 이유는 두 가지입니다. 

1. 자바스크립트 코드는 기업의 지식 재산에 해당합니다. 따라서 공격자 및 경쟁자로부터 최대한 보호해야 합니다. 

2. 데이터 무결성은 환경과 리스크 요인을 정확히 파악하는 데 필수입니다. 보호된 자바스크립트는 스크립트를 실행해 실제로 수집된 데이터이며 조작되거나 변형되지 않았기 때문에 신뢰할 수 있습니다.

클라이언트 측 코드 보호 및 데이터 진위성 확보 방법

보안의 다른 측면과 마찬가지로, 이 문제를 단일 솔루션으로는 해결할 수는 없습니다. 이 블로그 게시물에서는 Akamai가 자바스크립트 코드를 보호하고, 실행을 보장하며, 수집된 데이터의 진위성을 확보하기 위해 사용하는 다양한 방법을 소개합니다.

  • 코드 난독화
  • 데이터 무결성 검증
  • VM 난독화
  • 오해를 유도하는 코드 및 불필요한 코드 삽입
  • 자바스크립트 코드 순환
  • 동적 필드 순환
  • 자바스크립트 빌드 파이프라인 및 데이터 검증

코드를 보호하기 위해 유사한 방법을 적용하기로 결정했다면 팀, 기업, 기술 스택의 요구사항에 따라 이러한 방법의 조합을 사용하는 것을 권장합니다.

코드 난독화

난독화는 자바스크립트 코드를 보호하기 위해 가장 널리 사용되는 방법 중 하나입니다. 코드를 난독화하면 구조를 따라가거나 내용을 이해하기가 훨씬 어려워집니다.

개발 시에는 함수와 변수에 의미 있는 이름을 부여하고, 디버깅과 유지 관리가 용이하도록 논리적으로 구조화하는 것이 바람직한 개발 관행입니다. 이러한 방식은 시간과 노력을 절약할 수 있지만, 동시에 클린 코드는 리버스 엔지니어링에 더 쉽게 표적이 된다는 단점이 있습니다.

난독화를 적용하면 이러한 관행은 깨지게 되며, 의미 있는 이름의 변수와 함수가 무작위로 생성된 이름으로 대체됩니다. 경우에 따라 코드의 순서가 바뀌거나 인코딩되고, 일부 로직은 분할되기도 합니다. 이렇게 변경되더라도 웹 브라우저는 코드를 문제 없이 실행할 수 있고, 결과 또한 동일하게 유지됩니다. 그러나 해당 코드를 분석하거나 리버스 엔지니어링하려는 사람에게는 훨씬 더 어려운 작업이 됩니다.

개발자는 여전히 유지 관리와 개선을 위해 잘 구조화된 소스 코드를 사용하며, 새 버전을 배포하기 전 난독화 엔진을 통해 코드를 처리합니다. Code Beautify, JScrambler, Digital.ai 등 다양한 무료/오픈 소스 및 상업용 제품을 활용해 자바스크립트 코드를 쉽고 빠르게 난독화할 수 있습니다.

그림 1은 지문 인식 시 자주 사용되는 간단한 자바스크립트 함수의 예시입니다. 이 함수는 다양한 디바이스 특성을 추출하도록 설계되었으며, 난독화 이전의 원래 코드 형태를 보여줍니다.

  function getDeviceInfo() {
 return {
   userAgent: navigator.userAgent,
   hardwareConcurrency: navigator.hardwareConcurrency || "unknown",
   screenOrientation: screen.orientation.type,
 };
}

그림 1: 난독화 이전의 원본 코드

원 상태의 코드가 얼마나 이해하기 쉬운지 확인할 수 있습니다. 코딩에 대한 지식이 많지 않은 사람이라도 코드의 의도와 동작 방식을 쉽게 파악할 수 있습니다.

그림 2는 Code Beautify 온라인 툴을 통해 처리된 동일한 자바스크립트 함수입니다.

  (function(_0xbf521e,_0x43c80b){var _0x4ad763=_0x3e09,_0x18fc85=_0xbf521e();while(!![]){try{var_0x40d2a7=parseInt(_0x4ad763(0xfc))/(0x18d1+-0xe6d+-0xa63)+-parseInt(_0x4ad763(0xf6))/(0x2*-0x7e4+0x171a+-0x750)+-parseInt(_0x4ad763(0xfb))/(-0x2e7*-0xb+0x6b*0x1f+-0x2cdf)*(parseInt(_0x4ad763(0xef))/(0x40f*-0x4+-0x897+0x18d7))+-parseInt(_0x4ad763(0xf3))/(0x3*-0xb5f+0x462+0x1dc*0x10)*(parseInt(_0x4ad763(0xf0))/(-0xb87*-0x1+0x18e8+-0x3*0xc23))+-parseInt(_0x4ad763(0xfa))/(0x2258+0x8f7+-0x2b48)*(-parseInt(_0x4ad763(0xee))/(0x3e9+-0xe93+0xab2))+parseInt(_0x4ad763(0xf1))/(0x1*-0x81e+0x525*-0x5+0x4*0x878)+parseInt(_0x4ad763(0xed))/(-0x59*-0x1f+0x779+-0x6f*0x2a);if(_0x40d2a7===_0x43c80b)break;else _0x18fc85['push'](_0x18fc85['shift']());}catch(_0x4460fc){_0x18fc85['push'](_0x18fc85['shift']());}}}(_0x1950,-0x1f*-0x38cb+0x17f2fa+-0x10aebf));function getDeviceInfo(){var _0x7a196=_0x3e09,_0x52340e={'VEDsL':_0x7a196(0xf8)};return{'userAgent':navigator[_0x7a196(0xf4)],'hardwareConcurrency':navigator[_0x7a196(0xf2)+_0x7a196(0xfd)]||_0x52340e[_0x7a196(0xf5)],'screenOrientation':screen[_0x7a196(0xf9)+'n'][_0x7a196(0xf7)]};}function _0x3e09(_0x56cbb3,_0x1167d0){var _0xddc250=_0x1950();return _0x3e09=function(_0x363b57,_0x27d74c){_0x363b57=_0x363b57-(-0x6d9+0x1316*0x1+-0xb50);var _0x1b2eec=_0xddc250[_0x363b57];return _0x1b2eec;},_0x3e09(_0x56cbb3,_0x1167d0);}function _0x1950(){var _0x1d7105=['ncurrency','20162890GviEyp','2488DLGTpn','4rCTHCm','65154TKsGUe','7673175smCphy','hardwareCo','670lOXWEG','userAgent','VEDsL','1749116JlgXKK','type','unknown','orientatio','12971xihUJr','2027775PnQRTc','487370FufNiT'];_0x1950=function(){return _0x1d7105;};return _0x1950();}

그림 2: Code Beautify를 통해 난독화된 코드

난독화된 코드는 길이만 보더라도 훨씬 이해하기 어려운 형태라는 것을 알 수 있습니다. 복잡해 보이긴 하지만, 이러한 단순 난독화 기법을 되돌리는 방법은 이미 존재하며 이는 공격자들에게 잘 알려져 있습니다. 그럼에도 불구하고, 이 정도 수준의 난독화만으로도 기술 수준이 낮거나 경험이 부족한 공격자들은 어느 정도 차단할 수 있습니다.

보안에서 중요한 전략 중 하나는 공격자를 지치게 하거나, 성공적인 공격을 수행하는 데 필요한 실제 노력이나 예상되는 난이도를 높여 표적으로서 해당 기업의 매력을 떨어뜨리는 것입니다.

데이터 무결성 검증

앞서 본 것처럼 코드 난독화는 좋은 출발점이지만, 동기가 확실한 공격자를 막는 데 단독으로는 충분하지 않습니다. 난독화를 역전시켜 원 형식으로 복원하는 방법과 툴이 존재하기 때문입니다. 따라서 난독화 기법 외에도, 불필요한 코드 삽입 및 데이터 무결성 검증 기능을 함께 구축하면 수집된 정보의 무결성을 더욱 강화할 수 있습니다.

코드 및 데이터 무결성 검사는 스크립트가 생성한 출력이 실제로 유효한지 확인하기 위해 코드 곳곳에 삽입되는 간단한 함수입니다. 이 검사는 기존 핵심 자바스크립트 함수의 출력 결과와 사용자 세션에 고유한 시드 값을 포함한 여러 변수를 사용해 보조 출력을 생성합니다.

그림 3은 세 개의 변수를 입력으로 받아 간단한 수학적 공식과 해시 함수를 사용해 결과를 반환하는 함수의 예시입니다. 변수 a와 b는 두 개의 핵심 함수 출력에 해당할 수 있으며, 변수 c는 고유한 시드일 수 있습니다. 이 예시에서는 모든 속성이 숫자 값이어야 합니다.

  function IntegrityCheck(a, b, c) {
   const mathResult = a + b * c;
   const stringResult = String(mathResult);
   let hash = 0;
   for (let i = 0; i < stringResult.length; i++) {
    hash = (hash * 31 + stringResult.charCodeAt(i)) >>> 0; 
 }
   return hash;
}

그림 3: 다중 변수를 활용한 데이터 무결성 코드 예시

보다 구체적으로는, screen.colorDepthnavigator.hardwareConcurrency 속성(모두 숫자 값을 반환함)을 그림 3의 단순 함수에서 변수 a와 b로 사용할 수 있습니다. 이 함수는 실제로 숫자 값을 반환하는 속성에만 제한되지 않습니다. 어떤 값이라도 해시되어 정수로 변환된 후 무결성 검사 함수에 입력될 수 있기 때문입니다. 단순한 예시를 위해 숫자 속성을 사용한 것일 뿐입니다.

다양성을 위해 일부 무결성 검사 함수는 그림 4의 예시에서처럼 핵심 함수의 출력을 해시해 사용할 수 있습니다.

  import { createHash } from 'crypto';

function hashTwoVariables(a, b) {
 const concatenatedString = String(a) + String(b); 
 const hash = createHash('sha256').update(concatenatedString).digest('hex');
 return hash;
}

그림 4: 해시 출력 예시

핵심 데이터 포인트를 보호하기 위해 코드 전반에 걸쳐 배치된 수십 개의 작은 함수들이 각기 다른 작업을 수행하면서 핵심 함수의 다양한 출력을 활용할 수 있습니다. 최종 단계에서는 모든 지문 정보, 사용자 행동 데이터, 개별 무결성 검사 결과 등을 포함한 전체 페이로드에 "서명"을 적용할 수도 있습니다. 이 작업은 전체 페이로드를 해시한 뒤, 원래의 출력값과 비교하는 방식으로 수행됩니다. 발신자와 수신자 측의 해시값이 일치한다면, 해당 페이로드는 변조되지 않은 안전한 데이터로 간주됩니다.

VM 난독화

간단한 무결성 검사 함수는 단순히 노출하거나, 기본적인 난독화 방식만으로는 보호할 수 없습니다. 이럴 때 더 고급 기법인 VM(가상 머신) 난독화가 사용되며 이를 통해 공격자는 내부 동작을 파악하거나 유효한 페이로드를 생성하는 것이 훨씬 어려워집니다.

VM 난독화는 코드를 가상 머신 바이트코드로 변환합니다. 머신은 이 바이트코드를 해석할 수 있지만, 공격자가 리버스 엔지니어링으로 분석하기는 훨씬 어렵습니다.

여러 벤더사가 VM 난독화 방법을 제공하지만 VM 난독화는 모든 종류의 함수 논리를 지원하지 않을 수 있습니다. 따라서 VM 난독화를 사용할 때는 벤더사의 지침을 준수하고 코드의 회귀 테스트를 철저히 수행해야 합니다.

회귀 테스트는 VM 난독화뿐 아니라 전체 보안 관리에서도 유용하므로, 보안 프로세스에 적극 도입할 가치가 있습니다. 특히 VM 난독화는 코드 출력이 복잡해지기 때문에, 회귀 테스트와 함께 사용하는 것이 더욱 중요합니다.

오해를 유도하는 코드 및 불필요한 코드 삽입

코드를 리버스 엔지니어링하려는 공격자의 시도를 더욱 어렵게 만들기 위해, 핵심 로직과는 무관한 불필요한 코드를 추가하는 방식으로 방어 레이어를 강화할 수 있습니다. 이러한 코드는 공격자를 혼란스럽게 하고 좌절시켜, 결국 시도를 포기하도록 유도하는 데 목적이 있습니다.

이와 유사하게, 무결성 검사 함수의 구조를 다양화함으로써 역 난독화와 리버스 엔지니어링을 더욱 어렵게 만들 수 있습니다. 이를 달성하는 한 가지 방법은 서로 다른 구조를 갖지만 동일한 출력을 생성하는 여러 함수를 개발하는 것입니다.

기능적으로 동일하지만 구조적으로 다른 함수는 VM 난독화 처리 후 서로 다른 방식으로 인코딩되기 때문에 코드를 리버스 엔지니어링하기가 훨씬 복잡해집니다.

그림 5는 동일한 출력을 반환하지만 각각 구조가 조금씩 다른 세 가지 함수의 예시입니다.

  function IntegrityCheck_1(a, b) {
 return a + b * 1; 
}

function IntegrityCheck_2(a, b) {
 return a + 0 + b; 
}

function IntegrityCheck_3(a, b, c) {
 return a + b + c * 0; 
}

그림 5: 동일한 출력을 반환하는 서로 다른 세 가지 코드의 예시

자바스크립트 코드 순환

오해를 유도하는 코드, 고급 난독화, 무결성 검사를 구축하는 것도 효과적이지만, 공격자는 매우 집요할 수 있으며 시간과 노력, 기술이 뒷받침된다면 고정된 코드는 결코 리버스 엔지니어링될 수 있습니다. 이를 방지하려면 스크립트의 유효 기간 자체를 제한해야 합니다.

예를 들어, 동일한 기능을 수행하는 코드라도 수천 가지의 고유한 렌디션(iterations)을 생성하고, 각 자바스크립트 코드 릴리스마다 서로 다른 무결성 검사 함수를 적용한다고 상상해 보세요. 각 렌디션은 단 10~20분 동안만 유효하며, 클라이언트가 주기적으로 새로운 버전을 로드하도록 강제하는 제어 메커니즘을 통해 이전 버전은 빠르게 무효화됩니다.

이 방법의 목적은 코드의 복잡도를 극대화해 공격자의 분석 속도를 앞지르고 결과적으로 공격자가 코드를 직접 분석하는 대신 브라우저에서 자바스크립트를 실행하는 것 외에는 다른 방법이 없도록 만드는 것입니다.

동적 필드 순환

코드는 읽기 어렵고 해석하기 어려울 수 있지만 출력 결과와 수집 및 전송된 데이터를 분석하면 목적을 추론할 수 있습니다. 특히 디바이스나 브라우저 특성과 관련된 정보는 서버로 전송될 때 비교적 쉽게 식별됩니다

반면, 단순히 불리언 값을 반환하는 함수나 정수 값을 출력하는 무결성 검사 함수의 경우, 그 목적을 파악하기가 훨씬 어렵습니다.

공격자가 페이로드 구조를 예측하기 어렵게 만드는 한 가지 방법은 각 수집 데이터 포인트를 보고하는 필드 이름을 매번 바꾸고, 각 렌디션에서 페이로드 내에서 상대적 위치까지 변경하는 것입니다.

앞서 논의한 대로 각 자바스크립트 렌디션에는 고유한 코드 무결성 검사 세트가 있습니다. 또한 페이로드는 매번 다른 필드 이름을 사용하며 특정 데이터 포인트의 위치도 렌디션마다 변경됩니다.

필드 이름과 그 위치는 자바스크립트 빌드 시점에 사전 정의된 알고리즘에 따라 결정되며, 데이터를 처리하는 서버도 동일한 알고리즘을 실행해 정확한 위치에서 핵심 정보를 가져올 수 있습니다. 이는 봇 및 사기 탐지를 정확히 수행하는 데 필수적인 요소입니다.

그림 6은 각 필드와 위치가 렌디션마다 어떻게 달라질 수 있는지 보여줍니다. 필드 이름은 눈에 띄지 않도록 의미 없는 방식으로 지정해야 합니다.

  Payload Iteration #1

  mx01: [user-agent]
  mx02: [display-mode]
  mx03: [hardconcur]
  mx04: [pixelDepth]
  mx05: [language]
  mx06: [WebGL_Rend]
  mx07: [intg_chck_1]
 
  Payload Iteration #2

  yw01: [display-mode]
  yw02: [intg_chck_1]
  yw03: [user-agent]
  yw04: [pixelDepth]
  yw05: [hardconcur]
  yw06: [WebGL_Rend]
  yw07: [language]
  
  Payload Iteration #3

  za01: [language]
  za02: [WebGL_Rend]
  za03: [hardconcur]
  za04: [pixelDepth]
  za05: [intg_chck_1]
  za06: [user-agent]
  za07: [display-mode]

그림 6: 필드 이름 렌디션 예시

출력에 7개의 필드만 있는 경우(위 예시와 동일)에는 렌디션마다의 변화를 쉽게 확인할 수 있습니다. 하지만 수백 개의 데이터 포인트가 수집되고 반환되는 경우를 상상해 보세요.

자바스크립트 빌드 파이프라인 및 데이터 검증

자바스크립트 코드를 보호하고 수집된 데이터의 무결성을 보장하기 위해 위에서 소개한 다양한 방법을 적용하려면, 복잡한 빌드 파이프라인과 릴리스 프로세스를 구축해야 합니다. 먼저, 개발자는 원본의 형식이 잘 갖춰진 자바스크립트 파일을 업데이트하고, 기능 테스트 및 회귀 테스트를 수행합니다.

다음으로 개발자는 알고리즘을 사용해 수천 개의 렌디션을 생성합니다. 각 렌디션은 다음과 같은 고유한 특성을 갖습니다.

  • 핵심 자바스크립트에서 데이터 포인트를 변경하는 데이터 무결성 검사 기능, 수학적/해시 함수의 사용, 전체 로직 내 상대적 위치 
  • 오해를 유도하거나 사용되지 않는 코드 세트
  • 페이로드 출력 필드 이름
  • 페이로드 출력 필드 순서

이러한 고유한 구성요소가 생성되면 자바스크립트 파일 렌디션은 다음과 같은 프로세스를 거칩니다.

  • VM을 통해 데이터 무결성 검사 및 기타 핵심 기능을 난독화
  • 전체 코드 난독화
  • 렌디션을 웹 서버에 업로드

모든 렌디션이 생성되어 업로드되면 새로운 자바스크립트 세트를 프로덕션 환경에 적용해야 합니다. 이러한 변경 사항은 데이터를 수신하는 서버에서 실행 중인 봇 및 사기 탐지 엔진과 협조해 이루어집니다. 서버는 자바스크립트 빌드 시스템에서 사용된 알고리즘의 일부를 실행해야 다음을 수행할 수 있습니다.

  • 클라이언트가 오래된 버전이 아니라 현재 자바스크립트 렌디션의 페이로드를 전송하고 있는지 검증합니다
  • 생성된 자바스크립트 렌디션에 따라 페이로드의 다양한 필드를 파싱합니다
  • 동등한 함수를 실행해 코드 무결성 검사 값을 검증합니다

최종 난독화까지 적용된 최종 제품은 릴리스 전에 사전 프로덕션 환경에서 모든 구성요소가 동기화되어 예상된 결과를 생성하는지 철저히 테스트해야 합니다. 이를 위해서는 자바스크립트를 위한 다소 복잡한 빌드 워크플로우를 구축해야 합니다.

그러나 코드 내용이 경쟁사나 공격자에게 노출되지 않아야 하며 그 출력이 인터넷 사용자와 웹사이트의 보안에 영향을 미친다면, 이러한 노력은 충분히 가치 있는 일이 됩니다.

결론

클라이언트 측에서 실행되는 지문 및 텔레메트리 수집용 자바스크립트 코드와 봇 및 사기 탐지를 위한 맞춤형 로직은 보안이 강화되어야 합니다. 코드와 데이터를 보호하기 위한 여러 전략이 존재하지만, 한두 가지 전략만으로는 고도로 정교한 기법을 사용하는 공격자를 충분히 막아내기 어렵습니다.

클라이언트 측 코드와 그 페이로드를 안전하게 보호하려면, 코드 난독화, 오해를 유도하거나 사용되지 않는 코드 삽입, VM 난독화와 결합된 코드 무결성 검사 기능, 페이로드 구조 무작위화, 코드의 정기적인 업데이트 등 여러 방어 레이어와 기술을 포함하는 복합적인 전략이 필요합니다.

그림 7의 수식은 효과적인 보호를 실현하기 위해 구축해야 하는 복합적인 전략의 전체 구성을 간결하게 보여줍니다.

  [JS Code obfuscation[
  + Misleading code 
  + unused code
  + VM Obfuscation [code integrity check] 
  + unique field names
  + field relative position shift] 
  x  [Number of unique iterations] 
  + Limited version validity (10 minutes)
  + Force JS reload]

그림 7: 자바스크립트 보호 전략의 수식

궁극적으로 이 전략 조합은 클라이언트가 자바스크립트를 실행하도록 강제해, 데이터 조작이나 탐지 엔진 회피 가능성을 줄여줍니다. 개발 부담을 줄이기 위해 VM 난독화와 같은 가장 복잡한 단계에는 상용 솔루션을 활용하는 것을 강력히 권장합니다. 다만 코드 무결성 검사, 오해를 유도하는 코드 조각, 다중 렌디션과 같은 일부 전략은 공격자가 역 난독화 도구를 개발했을 경우에도 방어력을 유지할 수 있도록 자체 개발 및 유지 관리하는 것이 바람직합니다.



데이비드 세네칼(David Senecal)

에 의해 작성

David Sénécal

July 08, 2025

데이비드 세네칼(David Senecal)

에 의해 작성

David Sénécal

데이비드 세네칼(David Sénécal)은 Akamai에서 엔지니어링 디렉터(사기 및 악용 방지 담당)로 근무하고 있으며, The Reign of Botnets의 저자이기도 합니다. 그는 인터넷의 안전성을 개선하는 데 열정을 가지고 있으며 온라인 사기 및 봇 탐지 분야의 전문가입니다. 웹 성능, 보안, 기업 네트워킹 기술 분야에서 지원, 통합, 컨설팅, 개발, 제품 관리, 아키텍처, 리서치 등 다양한 역할을 맡으며 25년 이상의 경력을 쌓아왔습니다.