반응형

Node.js 코딩 테스트 문제를 풀다 보면 꼭 나오는 코드가 있다.
`fs.readFileSync(0)`그냥 외워서 써보기도 했지만, 
'왜 0일까?', '왜 split("\n")을 꼭 써야 하지?'가 반복적으로 문제를 풀면서 궁금해졌다. 

이 코드가 실제로 어디서 입력을 읽는지, 어떤 형태로 들어오는지(Buffer),
그리고 split("\n")을 왜 붙여야 줄 단위 입력이 제대로 되는지 파봤다.

 

요약

stdin = Standard Input (표준 입력)
프로그램이 '입력'을 받을 때 사용하는 기본 통로이다. 
Node.js에서는 fs.readFileSync(0)이 바로 이 표준 입력을 읽는 것이다. 
즉, `fs.readFileSync(0)`은 파일 디스크립터(File Descriptor) 0번(stdin)을 읽는 코드이다. 

🔍 한 문장 정리
Node.js에서 `fs.readFileSync(0)`은 표준 입력 전체를 버퍼로 읽고, `.toString()`으로 문자열화한 뒤, `.split("\n")`을 통해 줄 단위로 구분해야 여러 줄 입력을 올바르게 다룰 수 있게 한다. 

1. 파일 디스크립터(File Descriptor)와 stdin(Standard Input)의 관계

운영체제는 입출력을 ‘파일’처럼 다루기 때문에 각 통로에는 고유 번호(FD, File Descriptor)가 부여하여 파일 대신 '표준 입력' 통로를 읽도록 하는 것이다. 

fs.readFileSync(0)의 번호 의미
0 stdin (표준 입력, Keyboard 등)
1 stdout (표준 출력, console.log 등)
2 stderr (표준 에러)

 


 

2. Buffer 형태로 입력이 들어온다

Node.js는 I/O를 빠르게 처리하기 위해, 모든 입력 데이터를 2진(binary) 형태로 일단 메모리에 저장한다. 그것이 바로 Buffer(버퍼) 이다. 

예를 들어 아래처럼 입력을 받았다고 가정 해본다. 

C
89.84336
234.5678

이 입력을 `fs.readFileSync(0)`으로 읽으면 실제 내부엔 이렇게 저장된다. 

<Buffer 43 0a 38 39 2e 38 34 33 33 36 0a 32 33 34 2e 35 36 37 38>

여기서

  • 43은 'C'
  • 0a는 줄바꿈(\n)

38은 '8'
… 이런 식으로 아스키코드 값이 들어있다. 


3. `.toString()` : 버퍼 → 문자열

버퍼를 문자열로 바꾸면 이렇게 된다. 

"C\n89.84336\n234.5678"

겉보기엔 줄이 바뀐 것 같지만, 실제로는 \n이 포함된 하나의 긴 문자열이다. 


4. `.trim()` : 앞뒤 공백 제거

`.trim()`은 문자열 양 끝의 공백·줄바꿈을 제거한다. 그리고 중간의 \n은 그대로 남는다. 

"C\n89.84336\n234.5678"

 

5. `.split("\n")` : 줄 단위로 나누기

`.split("\n")`은 문자열을 줄바꿈(\n) 기준으로 자르게 된다. 즉, 사용자가 친 엔터 기준으로 배열이 만들어지는 것이다. 

"C\n89.84336\n234.5678".split("\n")
→ ["C", "89.84336", "234.5678"]

 


6.  `.split("\n")`이 없을 때는?

`.split("\n")`을 빼면 이렇게 된다. 

"C\n89.84336\n234.5678"

이건 하나의 문자열이 되어,

input[0]은 "C",
input[1]은 "\n",
input[2]는 "8" 처럼 문자 단위로만 접근이 가능하게 된다. 

즉, '줄'의 개념이 사라지는 것이다. 

 


7. 실제 입출력 순서 요약

const fs = require("fs");
let n = fs.readFileSync(0).toString().trim().split("\n");

let c = n[0];              // 문자
let a = Number(n[1]);      // 문자열 → 숫자
let b = Number(n[2]);      // 문자열 → 숫자

console.log(c);
console.log(a.toFixed(2)); // 소수 둘째 자리까지 반올림
console.log(b.toFixed(2));

입력이 다음과 같다면:

C
89.84336
234.5678

실행 과정은 아래와 같은 흐름을 보이게 된다. 

단계 처리 내용 결과
`fs.readFileSync(0)` 표준 입력 전체를 Buffer로 읽음 <Buffer ...>
`.toString()` Buffer → 문자열 "C\n89.84336\n234.5678"
`.trim()` 앞뒤 공백 제거 "C\n89.84336\n234.5678"
`.split("\n")` 줄 단위 분리 ["C", "89.84336", "234.5678"]
`Number()`, t`oFixed()` 각 줄 데이터 변환 및 출력 "C", 89.84, 234.57

 


비교 요약

구분 `split("\n")` 있음 vs. `split("\n")` 없음

데이터 형태 배열 (각 줄이 요소) 하나의 문자열
예시 값 ["C", "89.84336", "234.5678"] "C\n89.84336\n234.5678"
접근 방식 `n[0]` → 첫 줄, `n[1]` → 둘째 줄 `n[0]` → 문자 'C', `n[1]` → '\n'
사용 목적 여러 줄 입력 한 줄 입력
장점 줄 단위로 쉽게 접근 가능 문자 단위 처리 가능 (특수 경우)

 


결론

`fs.readFileSync(0)`은 표준 입력(stdin, 0번 파일 디스크립터)을 버퍼 형태로 읽어오는 코드이다. 

Node.js는 줄바꿈을 `\n` 문자로 포함한 하나의 문자열로 반환하므로, 줄 단위 입력을 처리하려면 반드시 `.split("\n")`이 필요한 것이다. 

즉, '사용자가 엔터로 구분한 입력'을 '프로그램이 배열로 구분해 다루게 만드는' 핵심 단계가 바로 `.split("\n")` 이라는 점이다!

 


참고자료

https://nodejs.org/api/fs.html#fsreadfilesyncpath-options

 

File system | Node.js v25.0.0 Documentation

File system# Source Code: lib/fs.js The node:fs module enables interacting with the file system in a way modeled on standard POSIX functions. To use the promise-based APIs: import * as fs from 'node:fs/promises';const fs = require('node:fs/promises');copy

nodejs.org

https://nodejs.org/api/process.html#processstdin

 

Process | Node.js v25.0.0 Documentation

Process# Source Code: lib/process.js The process object provides information about, and control over, the current Node.js process. import process from 'node:process';const process = require('node:process');copy Process events# The process object is an inst

nodejs.org

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/split

 

String.prototype.split() - JavaScript | MDN

If separator is a string, an Array of strings is returned, split at each point where the separator occurs in the given string. If separator is a regex, the returned Array also contains the captured groups for each separator match; see below for details. Th

developer.mozilla.org

https://nodejs.org/api/buffer.html

 

Buffer | Node.js v25.0.0 Documentation

Buffer# Source Code: lib/buffer.js Buffer objects are used to represent a fixed-length sequence of bytes. Many Node.js APIs support Buffers. The Buffer class is a subclass of JavaScript's class and extends it with methods that cover additional use cases. N

nodejs.org

 

반응형
반응형

2025년 웹접근성 주요 변경 사항 개요

2025년 1월부터 웹 콘텐츠 접근성 인증 기준이 기존 22개 항목에서 33개 항목으로 크게 확대되다. 이는 약 8년 만의 대폭 개정으로, 시대 변화와 기술 발전을 반영하여 강화된 규정들이다. 특히 음성 인식 지원, 터치 제스처 대체 수단 제공, 드래그 앤 드롭 대체 방식 등이 새롭게 의무화되었다.


1. 버튼 크기 및 인터랙션 변화

과거 웹접근성 지침에서 버튼의 최소 크기는 주로 44×44픽셀 정도였으나, 2025년 지침에서는 모바일 및 터치 환경에 맞춰 최소 48×48픽셀로 확대 권고되고 있다. 사용자가 손가락으로 정확히 누를 수 있도록 더 큰 크기가 요구되며, 버튼과 인터랙션 요소 사이에 충분한 여백도 강조된다. 

또한 스와이프, 핀치 줌 등 터치 제스처 중심인 모바일 환경에서 제스처 대체 수단도 반드시 제공해야 하며, 손목이나 손가락 움직임이 불편한 사용자를 위한 다양한 조작 방법이 포함되어야 합니다.

2. 음성 인식 및 대체 수단 강화

2024년 이전 지침에서는 음성 인식 기능 지원이 명시적 의무사항은 아니었으나, 2025년부터는 웹사이트 내 음성 명령 지원이 필수적으로 권고된다. ‘헤이 구글’, ‘시리야’와 같은 음성 명령이 웹 인터페이스와 자연스럽게 연동되어야 하며, 보조기기 사용자가 음성으로 웹 콘텐츠와 상호작용할 수 있도록 해야 한다. 

이와 함께 끌어다 놓기(드래그 앤 드롭) 기능은 대체 수단, 즉 클릭 등의 방법으로도 동일한 작업이 가능해야 하며, 단일 상호작용에만 의존하면 안 된다. ​

3. 명도 대비 및 텍스트 크기

기존에는 명도 대비 비율이 4.5:1 이상으로 권고되었으나, 2025년 업데이트에서는 중요 콘텐츠에 대해 7:1 이상의 높은 명도 대비 유지가 권장되어 시각장애 사용자의 가독성을 높여야 한다. 텍스트 크기 역시 반응형 환경 최적화를 위해 최소 16px에서 스케일 조정이 필수가 되었고, 모바일에서는 12~14px로 유동적으로 변경하는 것을 인정하지만 가독성 손실이 없도록 해야 한다. ​

4. KWCAG 2.2 및 한국형 웹 콘텐츠 접근성 지침

2025년부터는 국제 WCAG 2.2를 기반으로 한 한국형 웹 콘텐츠 접근성 지침(KWCAG 2.2)이 공식 적용된다. 이 지침은 한국어 환경에 맞춘 맞춤형 규정들로, 맞춤법, 발음, 조사 등 언어 특성을 고려한 접근성 요소가 포함되어 있으며, 공공기관과 민간기업 모두 필수 준수 대상이다.


정리:  과거와 현재 비교

항목2024년 이전 기준2025년 변경 기준
심사 항목 수 22개 33개로 확대
버튼 최소 크기 44×44 픽셀 48×48 픽셀 이상 권고
터치 제스처 대체 수단 권고 수준 필수 제공
음성 인식 지원 권고 수준 필수 연동 권장
드래그 앤 드롭 단일 방식 지원 가능 대체 수단 반드시 제공
명도 대비 비율 4.5:1 이상 권고 중요한 콘텐츠 7:1 이상 권장
텍스트 크기 고정 크기 또는 제한적 반응형 지원 최소 16px 이상, 모바일 유동적 조정 허용
 
 

웹 표준과 웹접근성은 단순 기술 기준이 아닌 모두에게 공평한 정보 접근권 보장을 위한 필수 요소이다. 2025년 변경 사항을 반영해 더욱 다양하고 편리한 사용자 경험을 설계, 개발해보자! ​

  1. https://www.witheni.com/kor/index.php?pCode=MN0000003&pg=4&mode=view&idx=6256
  2. https://pure-f.tistory.com/62
  3. http://kwacc.or.kr/Board/Notice/5082/Detail?page=1
  4. https://blog.naver.com/oncodenews/223919190305?fromRss=true&trackingCode=rss
  5. https://moo-you.tistory.com/1204
  6. https://jinbytes.com/entry/KWCAG-2025-%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1-%EC%B5%9C%EC%8B%A0-%EA%B8%B0%EC%A4%80-%EC%A0%95%EB%A6%AC
  7. https://www.wa.or.kr/board/view.asp?BoardID=0001&sn=32749
  8. https://comblindness.tistory.com/entry/%E2%9C%A8-2025%EB%85%84-%EC%B5%9C%EC%8B%A0-%EA%B5%AD%EB%82%B4%EC%99%B8-%EC%A0%91%EA%B7%BC%EC%84%B1-%EB%B2%95%EA%B7%9C-%EB%B0%8F-%EC%A0%95%EC%B1%85-%EB%B3%80%ED%99%94-%ED%95%B5%EC%8B%AC%EB%A7%8C-%EC%8F%99%EC%8F%99-feat-%EC%A0%9C%EB%AF%B8%EB%82%98%EC%9D%B4-%F0%9F%A4%96
  9. https://www.wa.or.kr/board/view.asp?sn=32036&page=1&search=&SearchString=&BoardID=0001&cate=
  10. https://blog.naver.com/agapeuni/223852360075?fromRss=true&trackingCode=rss
  11. https://uiweb.tistory.com/230
  12. https://www.w3.org/WAI/standards-guidelines/ko
  13. https://www.kioskui.or.kr/index.do?menu_id=00001200
  14. https://hcdl.mohw.go.kr/notice/detail/567/1
  15. https://m.nuli.navercorp.com/community/article/1133264
  16. https://time.ly/ko/blog/%EC%A0%91%EA%B7%BC%EC%84%B1-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B0%80%EC%9D%B4%EB%93%9C-%ED%8F%AC%EC%9A%A9%EC%A0%81%EC%9D%B8-%EC%9D%B4%EB%B2%A4%ED%8A%B8-%EA%B8%B0%ED%9A%8D/
  17. http://www.webwatch.or.kr/WA/010301.html?MenuCD=130
  18. https://developer.chrome.com/blog/new-in-web-ui-io-2025-recap?hl=ko
  19. https://itpe.jackerlab.com/entry/%EC%9B%B9-%EC%A0%91%EA%B7%BC%EC%84%B1-%EA%B0%80%EC%9D%B4%EB%93%9C%EB%9D%BC%EC%9D%B8Web-Accessibility-Guideline
  20. https://www.seo.incheon.kr/open_content/main/guidance/webpolicy.jsp

 

반응형
반응형

childNodes vs children의 핵심 차이

기본 개념

// HTML 구조 예시
const div = document.createElement('div');
div.innerHTML = `
    첫 번째 텍스트
    <p>단락 1</p>
    <!-- 주석입니다 -->
    <span>스팬 요소</span>
    마지막 텍스트
`;

// childNodes: 모든 종류의 자식 노드 (NodeList)
console.log(div.childNodes);
// NodeList(9) [
//   text (줄바꿈과 공백),
//   text ("첫 번째 텍스트"),
//   text (줄바꿈과 공백),
//   p,
//   text (줄바꿈과 공백),
//   comment ("주석입니다"),
//   text (줄바꿈과 공백),
//   span,
//   text ("마지막 텍스트")
// ]

// children: HTML 요소만 (HTMLCollection)
console.log(div.children);
// HTMLCollection(2) [p, span]

핵심 정리:

  • childNodes: 모든 것 (텍스트, 주석, 요소) → 정밀한 DOM 조작
  • children: HTML 요소만 → 일반적인 웹 개발
  • 대부분의 경우 children이 더 편리하고 직관적
  • DOM의 정확한 구조가 중요할 때만 childNodes 사용
반응형

'JS_개념' 카테고리의 다른 글

childNodes vs children  (0) 2025.09.19
2-1-5. Node와 Element의 차이에 대해 설명해주세요  (0) 2025.09.19
textContent  (0) 2025.09.19

반응형

textContent의 근본 개념

textContent노드와 그 자손들의 텍스트 콘텐츠를 순수 텍스트로 표현한 것입니다. HTML 태그는 모두 제거하고 오직 텍스트만 추출하거나 설정하는 속성입니다.

const div = document.createElement('div');
div.innerHTML = '<p>Hello <strong>World</strong>!</p>';

// textContent는 모든 텍스트만 추출
console.log(div.textContent);  // "Hello World!"

// innerHTML은 HTML 구조 포함
console.log(div.innerHTML);  // "<p>Hello <strong>World</strong>!</p>"

주요 사용 케이스

1. 안전한 텍스트 삽입 (XSS 방지)

사용자 입력을 받을 때 가장 중요한 용도입니다:

// ❌ 위험: 사용자가 스크립트를 삽입할 수 있음
userComment.innerHTML = userInput;  // "<script>alert('해킹!')</script>" 실행됨

// ✅ 안전: HTML로 해석되지 않고 순수 텍스트로 표시
userComment.textContent = userInput;  // 스크립트가 문자 그대로 표시됨

2. 텍스트만 추출하기

HTML 구조에서 순수 텍스트만 필요할 때:

// 복잡한 HTML 구조에서 텍스트만 추출
const article = document.querySelector('article');
const plainText = article.textContent;  // 모든 태그 제거, 텍스트만

// 예: 글자 수 세기
const charCount = article.textContent.length;

// 예: 검색 기능
if (article.textContent.includes('JavaScript')) {
    // 해당 article에 'JavaScript' 단어가 포함됨
}

3. 간단한 텍스트 업데이트

HTML 구조가 필요 없이 텍스트만 변경할 때:

// 버튼 텍스트 변경
button.textContent = '로딩중...';

// 에러 메시지 표시
errorDiv.textContent = '입력값이 올바르지 않습니다.';

// 카운터 업데이트
counterSpan.textContent = count.toString();

4. 텍스트 노드 직접 조작

// 텍스트 노드의 내용 변경
const textNode = element.firstChild;  // 첫 번째 텍스트 노드
if (textNode.nodeType === Node.TEXT_NODE) {
    textNode.textContent = '새로운 텍스트';
}

textContent vs innerHTML vs innerText

const div = document.createElement('div');
div.innerHTML = `
    <p>보이는 텍스트</p>
    <p style="display:none">숨겨진 텍스트</p>
`;

// innerHTML: HTML 태그 포함
console.log(div.innerHTML);  // 전체 HTML 구조

// textContent: 모든 텍스트 (숨겨진 것 포함)
console.log(div.textContent);  // "보이는 텍스트숨겨진 텍스트"

// innerText: 실제로 보이는 텍스트만
console.log(div.innerText);  // "보이는 텍스트"

실전 예시: 댓글 시스템

function addComment(userInput) {
    const comment = document.createElement('div');

    // 사용자 입력을 위한 별도 요소 생성
    const textContainer = document.createElement('p');
    textContainer.textContent = userInput;  // XSS 방지

    // 시간 정보 요소 생성
    const time = document.createElement('span');
    time.className = 'comment-time';

    // small 요소도 DOM으로 생성 (더 안전)
    const small = document.createElement('small');
    small.textContent = new Date().toLocaleString();
    time.appendChild(small);

    // 조립
    comment.appendChild(textContainer);
    comment.appendChild(time);
    commentList.appendChild(comment);
}

// 결과 HTML:
// <div>
//     <p>사용자의 댓글 내용</p>
//     <span class="comment-time">
//         <small>2024. 12. 27. 오후 3:30:00</small>
//     </span>
// </div>

핵심은 "HTML 태그 해석이 필요한가?"입니다:

  • 필요 없음 → textContent (더 안전하고 빠름)
  • 필요함 → innerHTML (XSS 위험 주의)

코드 상세 해석

실무에서 자주 쓰는 패턴

function addComment(userInput) {
    const comment = document.createElement('div');
    comment.className = 'comment-item';

    // HTML 템플릿 사용 (신뢰할 수 있는 데이터만)
    comment.innerHTML = `
        <div class="comment-content"></div>
        <span class="comment-meta">
            <small class="comment-time">${new Date().toLocaleString()}</small>
        </span>
    `;

    // 사용자 입력은 textContent로 안전하게 삽입
    comment.querySelector('.comment-content').textContent = userInput;

    commentList.appendChild(comment);
}

XSS 공격 시뮬레이션

// 악의적인 사용자 입력 예시
const maliciousInput = '<img src=x onerror="alert(\'해킹!\')">'; 

// ❌ innerHTML 사용 시 (위험!)
comment.innerHTML = maliciousInput;  
// 결과: 스크립트가 실행되어 alert 창이 뜸!

// ✅ textContent 사용 시 (안전!)
comment.textContent = maliciousInput;  
// 결과: "<img src=x onerror="alert('해킹!')">" 텍스트가 그대로 표시됨

완전한 실전 예시

class CommentSystem {
    constructor(container) {
        this.container = container;
    }

    addComment(userInput, userId) {
        // 입력값 검증
        if (!userInput.trim()) {
            alert('댓글을 입력해주세요');
            return;
        }

        // 댓글 요소 생성
        const comment = document.createElement('article');
        comment.className = 'comment';
        comment.dataset.timestamp = Date.now();

        // 안전한 템플릿 구조
        comment.innerHTML = `
            <div class="comment-header">
                <span class="comment-author"></span>
                <time class="comment-time">${this.formatTime(new Date())}</time>
            </div>
            <div class="comment-body"></div>
            <div class="comment-actions">
                <button class="btn-like">좋아요</button>
                <button class="btn-reply">답글</button>
            </div>
        `;

        // 사용자 입력은 textContent로 안전하게
        comment.querySelector('.comment-author').textContent = userId;
        comment.querySelector('.comment-body').textContent = userInput;

        // 이벤트 리스너 추가
        comment.querySelector('.btn-like').addEventListener('click', () => {
            this.likeComment(comment);
        });

        this.container.appendChild(comment);
    }

    formatTime(date) {
        const now = new Date();
        const diff = now - date;

        if (diff < 60000) return '방금 전';
        if (diff < 3600000) return `${Math.floor(diff/60000)}분 전`;
        if (diff < 86400000) return `${Math.floor(diff/3600000)}시간 전`;
        return date.toLocaleDateString();
    }

    likeComment(comment) {
        comment.classList.toggle('liked');
    }
}

// 사용
const commentSystem = new CommentSystem(document.getElementById('comments'));
commentSystem.addComment('좋은 글 감사합니다!', 'user123');

핵심 원칙:

  • 사용자 입력 → textContent (항상!)
  • 신뢰할 수 있는 구조 → innerHTML (조심스럽게)
  • 두 가지를 섞을 때는 구조를 먼저 만들고, 사용자 데이터를 나중에 삽입
반응형

'JS_개념' 카테고리의 다른 글

childNodes vs children  (0) 2025.10.14
childNodes vs children  (0) 2025.09.19
2-1-5. Node와 Element의 차이에 대해 설명해주세요  (0) 2025.09.19

+ Recent posts