AI가 만든 코드는 실행 전에 사람이 확인해야 합니다. 학습용 JavaScript 두 개를 비교해 일곱 가지 위험 신호가 어떻게 달라지는지 직접 검증했습니다.
화면이 정상으로 보이더라도 소스에 비밀값이 남아 있거나, 사용자 입력이 그대로 HTML과 코드로 실행되거나, 민감정보가 브라우저 저장소에 기록될 수 있습니다. 그래서 기능 확인과 보안 검토를 분리해 기록했습니다.
AI 코드를 바로 실행하면 안 되는 이유
코드 생성 도구는 빠르게 초안을 만들지만 프로젝트의 실제 데이터, 권한 구조, 배포 환경을 모두 이해한다고 볼 수 없습니다. 문법이 맞고 화면이 뜨는 것과 안전하게 운영할 수 있는 것은 다른 문제입니다. 특히 API 키, 로그인 토큰, 사용자 입력, 외부 요청을 다루는 부분은 실행 전에 별도로 읽어야 합니다.
OWASP Top 10:2025는 웹 애플리케이션의 주요 보안 위험을 정리한 공식 기준입니다. 이 글의 일곱 항목은 그 전체를 대신하는 진단표가 아니라 초보자가 생성된 JavaScript를 처음 검토할 때 눈에 띄는 신호를 놓치지 않도록 좁혀 만든 시작 체크리스트입니다.
검토 순서는 단순합니다. 먼저 실제 비밀값과 개인정보를 제거한 복제본에서 코드를 읽습니다. 다음으로 정적 검사와 테스트를 실행하고, 어떤 입력과 권한으로 결과가 달라지는지 확인합니다. 마지막으로 운영 환경에 필요한 서버 측 검증, 접근 제어, 로그, 의존성 점검을 별도로 진행합니다.
직접 만든 7개 항목 보안 검사 화면
검증 날짜: 2026년 9월 10일. 환경: Windows 11, Chrome 기반 브라우저, localhost 정적 웹 페이지. 외부 API와 사용자 계정은 연결하지 않았습니다. 화면의 DEMO_KEY_DO_NOT_USE는 동작 설명을 위한 가짜 문자열이며 실제 키나 토큰이 아닙니다.
검사기는 입력된 문자열에 특정 패턴이 있는지 확인하는 로컬 학습 도구입니다. 하드코딩된 문자열 비밀값, innerHTML, eval, 암호화되지 않은 HTTP 요청, 토큰의 localStorage 저장, 길이·공백 검증 부재, target="_blank" 링크의 격리 속성 부재를 순서대로 검사합니다.
첫 번째 예시에는 일곱 위험 신호를 의도적으로 모두 넣었습니다. “7개 항목 검사” 버튼을 누른 뒤 각 항목이 차례로 “확인” 상태가 됐고 최종 점수는 “0 / 7 통과”, 현재 판정은 “7개 위험 신호”로 표시됐습니다.
두 번째 예시는 런타임 비밀값 조회, 입력 공백 제거와 길이 제한, textContent, JSON.parse, HTTPS, 테마처럼 민감하지 않은 설정의 sessionStorage 저장, rel="noopener noreferrer"를 사용했습니다. 같은 버튼을 다시 누르자 일곱 항목이 모두 “통과”로 바뀌고 “7 / 7 통과”가 표시됐습니다.
영상은 같은 브라우저 세션에서 약 0.22초 간격으로 화면을 기록해 연결했습니다. 마지막 상태에서 통과 항목 7개, 점수 7/7, 안내 문구 “정적 규칙 7개 중 7개가 통과했습니다”를 확인했습니다. 검사기는 외부 전송이나 동적 실행 없이 예시 문자열의 패턴만 읽었습니다.
1. 소스에 비밀값을 직접 적지 않는다
API 키와 데이터베이스 비밀번호를 소스에 문자열로 넣으면 저장소, 배포 파일, 화면 캡처, 오류 로그를 통해 노출될 수 있습니다. 이미 공개된 키는 파일에서 지우는 것만으로 끝내지 말고 제공자 화면에서 폐기하고 새 키를 발급해야 합니다.
OWASP 비밀정보 관리 안내는 비밀값의 저장, 제공, 감사, 교체와 수명주기 관리를 설명합니다. 실제 프로젝트에서는 호스팅 서비스의 환경 변수나 비밀 관리자에서 값을 주입하고, 필요한 구성 요소에만 최소 권한으로 제공해야 합니다. 프런트엔드로 전달되는 값은 사용자가 볼 수 있으므로 서버 비밀값을 넣는 장소가 아닙니다.
2. 사용자 입력을 HTML이나 코드로 실행하지 않는다
사용자가 조절할 수 있는 문자열을 innerHTML에 넣으면 의도하지 않은 마크업이 만들어질 수 있습니다. 단순 텍스트를 표시하려는 경우 textContent처럼 텍스트로 취급하는 방법이 더 알맞습니다. 꼭 HTML을 받아야 한다면 프로젝트 상황에 맞는 검증된 정화 방법과 콘텐츠 보안 정책을 함께 검토해야 합니다.
eval은 전달된 문자열을 JavaScript로 평가합니다. URL, 폼, 저장된 사용자 데이터 같은 외부 입력과 결합하면 코드 실행 경계가 흐려집니다. JSON 문자열이라면 JSON.parse처럼 목적이 제한된 파서를 사용하고 예상한 필드와 자료형을 다시 검사합니다.
OWASP DOM 기반 XSS 예방 안내는 입력이 들어가는 문맥에 따라 다른 안전한 처리 방법이 필요하다고 설명합니다. 단순히 위험해 보이는 문자 몇 개를 지우는 방식은 다양한 문맥을 충분히 다루지 못합니다.
3. 입력은 가능한 한 이른 시점에 서버에서도 검증한다
이번 안전 예시는 공백을 제거하고 문자열 길이가 80자를 넘는지 확인합니다. 이것은 화면에서 빠른 피드백을 주는 작은 예시일 뿐입니다. 사용자는 브라우저의 JavaScript를 끄거나 요청을 직접 만들 수 있으므로 보안 판단은 서버에서도 반복해야 합니다.
OWASP 입력 검증 안내는 신뢰하지 않는 모든 입력을 가능한 한 일찍 검사하고, 문법적 형식뿐 아니라 업무 규칙에 맞는 의미도 확인하라고 설명합니다. 고정된 선택지는 허용 목록과 정확히 일치시키고, 문자열에는 필요한 최소·최대 길이와 허용 형식을 정합니다.
입력 검증만으로 XSS나 주입 취약점을 모두 막을 수는 없습니다. 데이터베이스 질의는 매개변수화하고, HTML 출력은 문맥에 맞게 인코딩하며, 권한 검사는 요청을 처리하는 서버에서 수행해야 합니다. 여러 방어선을 하나의 정규식으로 대신하려 해서는 안 됩니다.
4. 전송과 브라우저 저장 범위를 구분한다
로그인 정보와 개인정보를 주고받는 요청에는 HTTPS를 사용합니다. 개발 중 로컬 주소를 제외한 운영 API가 http://로 남아 있지 않은지 환경별 설정을 확인합니다. HTTPS는 전송 구간을 보호하지만 잘못된 권한이나 노출된 키 자체를 해결하지는 않습니다.
localStorage는 같은 출처의 스크립트에서 읽을 수 있고 명시적으로 지울 때까지 남을 수 있습니다. 따라서 인증 토큰을 무조건 넣는 기본 저장소로 생각하면 안 됩니다. 이번 안전 예시는 보안 자격 증명이 아니라 화면 테마를 sessionStorage에 저장해 저장 목적의 차이를 보여 줍니다. 실제 인증 설계는 위협 모델, 쿠키 속성, 만료와 폐기, XSS 방어를 함께 검토해야 합니다.
새 창 링크에서 target="_blank"를 쓸 때는 rel="noopener noreferrer"를 함께 명시해 열린 페이지와 원래 페이지의 관계를 제한합니다. 브라우저의 현재 기본 동작만 기대하기보다 코드에 의도를 드러내는 편이 검토와 호환성 확인에 도움이 됩니다.
실제 프로젝트에 적용하는 검토 순서
첫째, 생성된 코드에서 키, 토큰, 이메일, 운영 URL처럼 공개되면 안 되는 값을 찾습니다. 실제 값이 보였다면 캡처나 외부 AI 대화에 다시 붙이지 말고 즉시 노출 범위를 확인하고 교체합니다. 예시 데이터를 만들 때도 실제 형식과 다른 분명한 가짜 값을 사용합니다.
둘째, 외부 입력이 어디서 들어와 어디로 이동하는지 따라갑니다. URL, 폼, 파일, API 응답, 브라우저 저장소의 값이 HTML, 데이터베이스 질의, 명령어, 리다이렉트에 들어가는 지점을 표시합니다. 입력 검증과 출력 처리, 서버 권한 검사가 각각 있는지 확인합니다.
셋째, 정상 입력과 실패 입력을 분리해 테스트합니다. 빈 값, 너무 긴 값, 예상하지 않은 자료형, 권한이 없는 사용자, 만료된 토큰, 느리거나 실패하는 외부 요청을 포함합니다. 테스트가 없으면 먼저 실패하는 사례를 재현하고, 최소 수정 뒤 같은 사례가 통과하는지 확인합니다.
넷째, 자동 검사를 실행합니다. 프로젝트의 린터와 테스트, 의존성 취약점 검사, 비밀값 탐지, 배포 전 빌드를 사용하되 결과를 무조건 신뢰하지 않습니다. 경고를 제외했다면 이유와 영향 범위를 기록하고, 운영에 중요한 변경은 코드 리뷰와 전문 점검을 추가합니다.
이번 검사의 한계와 해석법
화면의 7/7 통과는 제시한 문자열 패턴이 보이지 않는다는 뜻입니다. 애플리케이션 전체가 안전하다는 인증이 아닙니다. 문자열을 다른 방식으로 작성하면 위험 코드가 검사를 피할 수 있고, 반대로 학습용 문자열이 경고로 잡힐 수도 있습니다. 인증·인가, 서버 설정, 데이터베이스, 공급망, 업무 로직, 클라우드 권한, 실제 공격 가능성은 검사하지 않았습니다.
이 도구는 브라우저에서 코드를 실행하지 않는 정적 휴리스틱 검사입니다. 전문 보안 감사, 침투 테스트, 위협 모델링을 대체하지 않습니다. 결제, 건강정보, 계정 복구, 관리자 권한처럼 영향이 큰 기능은 경험 있는 보안 담당자의 검토와 서비스별 공식 지침을 적용해야 합니다.
또한 안전한 대안은 프로젝트 환경에 따라 달라집니다. 비밀값을 런타임에 가져온다는 한 줄만으로 접근 제어와 교체가 완성되는 것은 아니며, textContent도 텍스트가 아닌 URL·CSS·스크립트 문맥을 모두 해결하지 않습니다. 체크리스트는 질문을 시작하는 도구로 사용해야 합니다.
결론: 생성보다 검증 기록을 남긴다
AI 코딩에서 중요한 것은 코드를 얼마나 빨리 받았는지가 아니라 어떤 조건으로 검사했고 무엇이 남았는지 설명할 수 있는가입니다. 비밀값, 입력 경계, HTML과 코드 실행, 전송 방식, 저장 범위, 링크 격리를 먼저 확인하면 초보 프로젝트의 눈에 띄는 실수를 줄일 수 있습니다.
작은 기능을 직접 만들고 브라우저에서 동작을 검증하는 흐름은 프롬프트로 타이머 웹앱을 만든 실전 기록에서 이어서 볼 수 있습니다. 이 사이트가 어떤 기준으로 실험과 한계를 기록하는지는 소개 페이지에 정리했습니다.