Devin.KR

값과 조건의 기초

120분 안팎

학습 목표

문자열·숫자·불리언과 조건식으로 행사 공개 여부를 판단합니다.

개념

왜 공개와 잔여 정원을 따로 봅니까

행사 카드가 보인다고 참여 신청을 받을 수 있는 것은 아닙니다. 운영자가 비공개로 돌렸거나 정원이 찼을 수 있습니다. 숫자를 화면 문구로 만들기 전에 데이터의 의미를 조건으로 표현해야 합니다. 이번 레슨에서는 공개 상태와 두 수량을 받아 참여 가능 여부를 불리언으로 반환합니다. 신청 버튼이나 서버 저장은 다루지 않으며 판정 규칙만 독립적으로 연습합니다.

const는 변수 이름을 처음 값에 연결하고 그 이름에 다른 값을 다시 대입하지 못하게 합니다. let은 이후 재대입이 필요한 변수에 사용합니다. 문자열은 따옴표로 감싸고 숫자는 따옴표 없이 씁니다. true와 false는 불리언 리터럴입니다. JSON의 capacity:3과 capacity:"3"은 서로 다른 타입입니다. API나 폼에서 들어온 값이 같아 보인다고 같은 데이터라고 취급하지 않습니다.

published는 불리언 true일 때만 공개입니다. 문자열 "true"나 "false"를 if에 바로 넣으면 두 값 모두 비어 있지 않은 문자열이라 참으로 평가됩니다. 따라서 event.published === true처럼 타입과 값을 함께 비교합니다. =는 대입이고 ===는 비교입니다. ==는 변환이 끼어들 수 있어 이번 데이터 계약에는 쓰지 않습니다. if는 조건이 참일 때 블록을 실행하며 else는 나머지 경우를 맡습니다.

event는 행사 속성을 묶은 객체이며 event.capacity는 그 객체의 capacity 값을 읽습니다. 아래 function canJoin(event) 블록은 이번 판정을 실행하기 위한 틀입니다. return 뒤의 조건 결과를 호출자에게 돌려줍니다. 지금은 조건을 바꾸고 결과를 비교하는 데 집중하고, 함수의 인자와 반환 책임은 다음 레슨에서 자세히 연습합니다. Number.isInteger도 값을 받아 참 또는 거짓을 돌려주는 함수입니다.

계약을 코드로 옮깁니다

capacity는 양의 정수이고 registered는 0 이상의 정수입니다. Number.isInteger는 숫자 타입의 정수인지 확인하며 문자열 숫자를 변환하지 않습니다. 두 값이 정상인 경우 registered < capacity로 여석을 확인합니다. 등록자 수가 정원과 같거나 넘으면 false입니다. 정원이 0이면 모집하지 않는 행사로 판단합니다. 음수나 소수 수량을 정상으로 통과시키지 않습니다.

&&는 모든 조건이 만족되는지 순서대로 평가할 수 있습니다. 왼쪽이 거짓이면 오른쪽을 실행하지 않는 단축 평가를 합니다. 일반적으로 &&의 결과가 항상 불리언인 것은 아니지만 여기서는 각 항이 비교나 Number.isInteger 결과이므로 최종 결과도 불리언입니다. ||로 공개와 잔여 정원을 연결하면 한 조건만 만족해도 통과하므로 요구와 다릅니다.

입력은 JSON 객체이며 published, capacity, registered를 검사합니다. JSON 자체는 유효한 형식이라고 가정하지만 필드 누락이나 잘못된 필드 타입은 참여 불가로 처리합니다. null이나 배열을 최상위 행사로 주는 입력은 이번 계약에 없습니다. 형식 계약과 업무 규칙을 구분하면 잘못된 값에 대한 처리와 호출자 책임을 함께 설명할 수 있습니다.

function canJoin(event) {
  return event.published === true
    && Number.isInteger(event.capacity) && event.capacity > 0
    && Number.isInteger(event.registered) && event.registered >= 0
    && event.registered < event.capacity;
}

경계에서 판정을 확인합니다

정원 바로 아래, 같은 값, 바로 위를 차례로 넣습니다. 4명 정원에 3명 등록은 true, 4명과 5명 등록은 false입니다. 공개가 false라면 여석이 있어도 false입니다. 문자열 수량은 자동 변환하지 않고 거절합니다. 이렇게 경계값과 독립된 조건을 분리해 시험하면 한 예제가 우연히 통과하는 상황을 줄일 수 있습니다.

잘못된 숫자를 0으로 덮어 버리면 수량 누락과 실제 0명을 구분하지 못합니다. 이번 구현은 유효하지 않은 수량을 false로 판정합니다. 이 결과는 참여 가능성일 뿐 데이터 오류 상세 안내는 아닙니다. 운영 화면에서 이유를 나눠 보여 줄 필요가 생기면 반환 형식을 바꾸고 테스트와 호출자를 함께 변경합니다. 지금은 불리언 하나라는 약속을 유지합니다.

흔한 실수와 오류 읽기

TypeError나 ReferenceError가 나오면 메시지의 변수 이름과 첫 번째 작성 파일 줄을 확인합니다. ReferenceError는 정의하지 않은 이름이나 철자 차이를 의심합니다. SyntaxError는 따옴표·괄호·쉼표 구조를 먼저 봅니다. 테스트가 false를 기대했는데 true라면 공개 문자열, 정원 경계, 논리 연산 순서를 확인합니다. 런타임 오류와 업무 판정 불일치는 해결 위치가 다릅니다.

입력과 출력을 직접 연결합니다

브라우저 채점의 JavaScript 코드는 웹페이지의 DOM 스크립트가 아니라 Node 환경에서 표준 입력을 읽는 프로그램입니다. require("fs").readFileSync(0, "utf8")는 입력 텍스트를 읽고 JSON.parse는 객체나 배열로 해석합니다. 계산 함수의 반환값은 JSON.stringify로 직렬화한 뒤 console.log 한 번으로 출력합니다. 입력 파싱, 계산, 출력 세 부분을 나누면 같은 계산 함수를 미션의 브라우저 코드에서도 재사용할 수 있습니다.

JSON 속성 이름과 문자열은 큰따옴표로 감싸며 마지막 속성 뒤에 쉼표를 남기지 않습니다. JSON.parse의 SyntaxError는 계산 규칙이 아니라 입력 형식 문제일 수 있습니다. 테스트 입력을 그대로 복사해 먼저 실행하고 이후 한 필드씩 바꿉니다. 객체를 그대로 console.log하면 Node의 관찰용 표현이 출력되므로 채점 계약의 JSON과 다를 수 있습니다. 기대 출력에 맞춰 직렬화한 문자열을 내보냅니다.

실습을 끝내는 기준

starter의 TODO에 계산을 구현하고 정상 사례와 경계 사례를 모두 실행합니다. 정답 한 건을 하드코딩하면 다른 입력에서 실패합니다. 각 실패에서 입력, 기대값, 실제값을 적고 원인이 된 조건을 고칩니다. 테스트 자체나 기대값을 바꾸어 통과시키지 않습니다. solution은 막혔을 때 자신의 조건과 비교할 자료이며 복사 전에 입력을 손으로 추적해 어느 판단이 달랐는지 설명합니다.

더 읽기의 서재 장은 문법을 넓히는 참고 자료입니다. 이 레슨은 지역 행사 데이터 계약을 직접 구현하는 데 집중합니다. 완료 후에는 새 입력을 스스로 만들고 결과를 예상한 다음 실행 결과와 대조합니다. 아래 미션에서는 앞 모듈의 HTML과 CSS를 보존한 채 데이터 파일과 함수를 추가합니다. 계산과 화면 연결을 나누면 이후 DOM 이벤트와 API 모듈에서도 검증한 규칙을 유지할 수 있습니다.

따라하기

정상 입력 추적

계산 결과를 먼저 예상합니다. 아래 코드를 Node의 실행 창이나 별도 example.cjs 파일에 입력합니다. 파일 실행 명령은 node example.cjs입니다. 각 단계는 독립된 예제이므로 이전 변수 선언과 섞지 않습니다.

function canJoin(event) {
  return event.published === true
    && Number.isInteger(event.capacity) && event.capacity > 0
    && Number.isInteger(event.registered) && event.registered >= 0
    && event.registered < event.capacity;
}
console.log(canJoin({published:true, capacity:4, registered:3}));

실행 결과

true

경계와 빈 값 확인

조건 하나를 바꿔 결과를 비교합니다. 아래 코드를 Node의 실행 창이나 별도 example.cjs 파일에 입력합니다. 파일 실행 명령은 node example.cjs입니다. 각 단계는 독립된 예제이므로 이전 변수 선언과 섞지 않습니다.

function canJoin(event) {
  return event.published === true
    && Number.isInteger(event.capacity) && event.capacity > 0
    && Number.isInteger(event.registered) && event.registered >= 0
    && event.registered < event.capacity;
}
console.log(JSON.stringify([4,5].map(n => canJoin({published:true,capacity:4,registered:n}))));

실행 결과

[false,false]

호출 계약 점검

원본과 반환값의 책임을 확인합니다. 아래 코드를 Node의 실행 창이나 별도 example.cjs 파일에 입력합니다. 파일 실행 명령은 node example.cjs입니다. 각 단계는 독립된 예제이므로 이전 변수 선언과 섞지 않습니다.

function canJoin(event) {
  return event.published === true
    && Number.isInteger(event.capacity) && event.capacity > 0
    && Number.isInteger(event.registered) && event.registered >= 0
    && event.registered < event.capacity;
}
console.log(canJoin({published:"false",capacity:4,registered:0}));

실행 결과

false

브라우저 실습과 미션으로 옮기기

입력은 행사 JSON 객체입니다. published는 불리언 true, capacity는 양의 숫자 정수, registered는 0 이상의 숫자 정수이며 정원 미만일 때만 true를 출력합니다. 필드 누락·잘못된 타입·소수·음수는 false입니다. 최상위 입력은 null이 아닌 객체입니다.

아래 실습 starter의 TODO를 구현하고 모든 제공 입력을 실행합니다. 출력은 JSON 한 개만 남깁니다. Node 로컬에서는 main.cjs에 코드를 저장하고 다음 명령으로 입력 파일을 전달할 수 있습니다. input.json에는 아래 tests의 입력 한 개를 저장합니다.

node main.cjs < input.json

미션에서는 계산 함수를 assets/data.js에 옮깁니다. 표준 입력용 require와 console.log는 브라우저에 복사하지 않습니다. npm ci 후 npm test로 데이터 계약과 앞 모듈 문서·CSS 검사를 확인합니다.

확인 문제

실습

입력은 행사 JSON 객체입니다. published는 불리언 true, capacity는 양의 숫자 정수, registered는 0 이상의 숫자 정수이며 정원 미만일 때만 true를 출력합니다. 필드 누락·잘못된 타입·소수·음수는 false입니다. 최상위 입력은 null이 아닌 객체입니다.

모범 답안
const input = JSON.parse(require('fs').readFileSync(0,'utf8'));
function canJoin(event) {
  return event.published === true
    && Number.isInteger(event.capacity) && event.capacity > 0
    && Number.isInteger(event.registered) && event.registered >= 0
    && event.registered < event.capacity;
}
console.log(JSON.stringify(canJoin(input)));

더 읽기

면접 질문

  • ===와 ==의 차이는 무엇이며 공개 상태 검사에 ===를 쓰는 이유는 무엇인가요?
  • 논리 AND의 단축 평가는 어떻게 동작하나요?