값과 비교의 기본
75분 안팎
학습 목표
문자열·숫자·불리언·null과 엄격 비교를 구분합니다.
개념
화면에 같은 숫자로 보여도 계약은 다릅니다
앞 모듈에서 회원 수가 0에서 1로 바뀌었는지를 눈으로 비교했습니다. 자동화에서는 숫자 1과 문자열 "1"을 같은 값으로 받아들이면 응답 타입이 바뀐 결함을 놓칩니다. QA의 검증 코드는 제품이 보낸 값을 편하게 고치는 코드가 아니라 합의한 자료형과 의미를 확인하는 코드입니다. 값의 모양보다 타입과 필수 여부를 먼저 분리하면 실패 원인도 구체적으로 전달할 수 있습니다.
이번 목표는 JavaScript로 가입 결과 객체를 읽고 잘못된 필드를 찾아내는 것입니다. 프로그램 작성 경험이 없더라도 변수에 관찰값을 담고 조건으로 비교하는 순서를 따라갑니다. 모든 언어 문법을 한꺼번에 외우지 않습니다. 회원 수·응답 판정·요청 ID처럼 이번 프로젝트에 쓰이는 값부터 다룹니다. 문자열과 숫자가 섞이는 일반 형변환 규칙은 더 읽기의 서재 장에서 확장합니다.
이 모듈에서 사용하는 실행 방식
브라우저 실습은 제공 실행 창에서 JavaScript를 선택하고 표준 입력에 JSON 한 개를 넣습니다. DOM이나 외부 네트워크는 사용하지 않습니다. 코드의 require('fs')는 실행 환경에서 표준 입력을 읽는 준비 코드이며 화면 입력칸을 직접 조작하는 코드가 아닙니다. JSON.parse가 읽은 값을 검증하고 console.log로 결과 한 줄을 출력합니다. 시작 코드의 입력 부분은 그대로 두고 TODO 아래를 작성합니다.
로컬 실습은 Node.js 20 이상에서 압축을 푼 실습 루트의 bash check.sh를 실행합니다. 외부 패키지는 필요하지 않습니다. 명령을 처음 사용한다면 터미널에서 해당 폴더로 이동하고 checks 폴더와 check.sh가 있는지 확인합니다. 브라우저 실행 결과와 로컬 node:test 결과는 출력 형식이 다르지만 기대값을 독립적으로 비교한다는 목적은 같습니다. 실제 회원이나 운영 설정은 입력하지 않습니다.
변수와 객체를 읽는 최소 문법
const count = 1은 count라는 이름에 숫자 1을 연결합니다. 문자열은 따옴표로 감싸고 불리언은 true 또는 false를 따옴표 없이 씁니다. null은 값이 없음을 명시한 값입니다. const로 선언한 이름에는 다른 값을 재대입하지 않지만 그 이름이 가리키는 객체의 속성까지 자동으로 잠기지는 않습니다. 실습에서는 응답 객체를 고치지 않고 읽기만 하므로 원본 증거도 보존할 수 있습니다.
객체는 result·count·requestId 같은 이름과 값의 묶음입니다. body.result는 body에서 result 속성을 읽습니다. 속성이 빠지면 undefined가 나오며 null과는 구별합니다. 배열은 여러 항목의 순서 있는 묶음으로 대괄호를 사용합니다. 객체 검증에 배열을 허용할지는 계약으로 정합니다. 이번 가입 결과는 한 객체만 허용하므로 Array.isArray로 배열을 거절합니다.
function inspect(body)는 body라는 입력을 받아 검사하는 함수의 시작입니다. return은 함수 결과를 돌려주고 그 호출의 실행을 끝냅니다. if는 괄호 안 조건이 참일 때 중괄호 안을 실행합니다. 각 조건에서 실패 필드 이름을 return하면 뒤 검사로 넘어가지 않습니다. 이번 실습의 첫 실패 순서는 body, result, count, requestId로 정해져 있어 여러 오류가 있어도 같은 진단이 나옵니다.
엄격 비교로 계약을 보호합니다
===는 타입이 다르면 같다고 판단하지 않습니다. 숫자 1과 문자열 "1"을 비교하면 false입니다. 반면 ==는 이 조합에서 형변환 후 같다고 판단합니다. 성공 판정이 문자열 VALID인지 비교할 때는 body.result === 'VALID'처럼 씁니다. 숫자로 바꿀 수 있는 문자열이라도 이번 count 계약에서는 거절합니다. Number(body.count)로 먼저 바꾸면 원래 응답 타입이 틀렸다는 정보를 잃습니다.
typeof는 값의 타입 이름을 문자열로 반환합니다. 숫자에는 number, 문자열에는 string, 불리언에는 boolean이 나옵니다. 주의할 예외는 typeof null이 object라는 점입니다. 그래서 null 여부를 먼저 확인하고 object인지 확인한 뒤 배열을 제외합니다. null에서 속성에 접근하면 Cannot read properties of null 같은 TypeError가 나올 수 있습니다. 그런 실행 오류를 필드 검증 실패와 혼동하지 않습니다.
조건을 연결할 때 &&는 앞 조건이 참일 때 뒤를 평가하고 ||는 앞 조건이 거짓일 때 뒤를 평가합니다. body !== null && typeof body === 'object'는 null을 제외한 뒤 객체 여부를 검사합니다. count가 숫자라는 조건만으로 충분하지 않습니다. Number.isSafeInteger는 소수·NaN·Infinity와 안전하게 표현할 수 없는 정수를 제외합니다. 여기에 count >= 0을 붙여 회원 수의 도메인 조건을 표현합니다.
필수와 참 같은 값을 구별합니다
if (body.count)만 쓰면 정상 회원 수 0도 거짓으로 평가되어 누락으로 오해합니다. 반대로 문자열 "false"는 빈 문자열이 아니어서 참 같은 값입니다. 불리언 계약에서는 value === true 또는 typeof value === 'boolean'처럼 의도를 드러냅니다. 필수 숫자, 필수 문자열, 성공 여부는 서로 다른 질문입니다. 짧은 조건 하나로 모두 처리하기보다 어떤 규칙을 검사하는지 이름으로 설명합니다.
요청 ID는 문자열이면서 공백뿐이지 않아야 합니다. typeof 검사를 통과한 다음 trim().length를 읽습니다. 타입 확인 전에 trim을 부르면 숫자에서 trim is not a function 오류가 나옵니다. 원본 ID를 trim한 값으로 덮어쓰지는 않습니다. 공백이 아닌지 판단하는 것과 문자열을 정규화하는 것은 목적이 다릅니다. 공백 포함 여부까지 제한하려면 별도 합의가 필요합니다.
문법 오류는 입력을 읽는 단계에서 구분합니다. JSON 키와 문자열에는 큰따옴표를 쓰며 마지막 속성 뒤에 쉼표를 붙이지 않습니다. Unexpected token 등의 SyntaxError가 나오면 값 검증 함수를 고치기 전에 JSON의 따옴표·쉼표를 확인합니다. ReferenceError는 선언하지 않은 이름이나 철자 차이를 살펴봅니다. 출력에 실패 필드가 나오면 그 필드의 원래 값과 계약을 함께 읽습니다.
검사 순서를 직접 설명합니다
실습에서는 본문 형태, result의 문자열 타입, count의 안전한 비음수 정수, requestId의 공백 아닌 문자열 순으로 검사합니다. result가 어떤 판정인지는 다음 레슨에서 기대값과 비교합니다. 이번에는 거절 판정 INVALID도 문자열이면 형태 검사에 통과합니다. 형태 검사가 PASS라고 가입 성공을 뜻하지 않는다는 구분이 필요합니다. 표현 가능한 데이터와 요구사항을 만족하는 데이터는 다른 단계입니다.
제출 전 정상 객체, 회원 수 0, 문자열 회원 수, null, 배열, 누락 ID를 차례로 입력합니다. 한 번 통과했다고 다른 타입에서도 안전한지 알 수 없습니다. 각 입력에 대해 어느 검사가 먼저 실패해야 하는지 말하고 실제 출력을 대조합니다. 타입을 바꾸어도 통과한다면 강제 변환이나 참 같은 값 검사가 숨어 있는지 확인합니다. 이 판단을 할 수 있으면 다음 레슨에서 인수 기준을 함수로 옮길 준비가 됩니다.
따라하기
타입과 엄격 비교
숫자처럼 보이는 문자열을 숫자와 비교합니다. 두 연산자의 결과가 왜 다른지 타입으로 설명합니다.
console.log(typeof 1, typeof '1', typeof true);
console.log(1 === '1', 1 == '1');실행 결과
number string boolean false true
null과 배열 구별
null을 객체 검사 전에 제외하고 배열도 거절합니다. 마지막 입력만 가입 결과 형태입니다.
for (const x of [null, [], {result:'VALID'}]) {
console.log(typeof x, x !== null && typeof x === 'object' && !Array.isArray(x));
}실행 결과
object false object false object true
0과 문자열 false 읽기
회원 수 0은 정상이고 문자열 false는 실제 불리언 false가 아닙니다. 참 같은 값만으로 검사하지 않습니다.
const count=0; const active='false';
console.log(Boolean(count), Number.isSafeInteger(count));
console.log(Boolean(active), active === true);실행 결과
false true true false
검사 함수 만들기
함수 입력을 읽고 첫 실패를 반환합니다. 이 코드는 숫자 타입만 보여 주는 작은 예제이며 안전 정수·필수 값은 실습에서 추가합니다.
function inspect(x) {
if(x===null || typeof x!=='object' || Array.isArray(x)) return 'FAIL body';
if(typeof x.count!=='number') return 'FAIL count';
return 'PASS';
}
console.log(inspect({count:0})); console.log(inspect({count:'0'}));실행 결과
PASS FAIL count
확인 문제
실습
JSON 한 개의 형태를 검사합니다. 본문은 배열 아닌 객체, result는 문자열(값 자체는 비교하지 않음), count는 0 이상의 안전한 정수, requestId는 공백 아닌 문자열입니다. 검사 순서 body→result→count→requestId로 첫 오류만 FAIL 필드명으로 출력하고 모두 만족하면 PASS를 출력합니다. 원본 값은 변환하지 않습니다.
모범 답안
const fs=require('fs');
const value=JSON.parse(fs.readFileSync(0,'utf8'));
function inspect(x){
if(x===null||typeof x!=='object'||Array.isArray(x))return 'FAIL body';
if(typeof x.result!=='string')return 'FAIL result';
if(!Number.isSafeInteger(x.count)||x.count<0)return 'FAIL count';
if(typeof x.requestId!=='string'||x.requestId.trim().length===0)return 'FAIL requestId';
return 'PASS';
}
console.log(inspect(value));
더 읽기
면접 질문
- 가입 폼의 테스트 조건을 정하는 과정을 설명해 주시면 됩니다.