연습 - 파일 시스템 작업

완료됨

Tailwind Traders는 전 세계에 많은 오프라인 매장이 있습니다. 매일 밤, 매장에서 전날의 모든 매출 합계를 포함하는 sales.json 파일을 만듭니다. 해당 파일은 매장 ID별로 명명된 폴더에 구성됩니다.

이 연습에서는 폴더에서 sales.json이라는 파일을 검색할 수 있는 Node.js 프로그램을 작성합니다.

개발 컨테이너에서 프로젝트 열기

  1. MicrosoftDocs/node-essentials GitHub 리포지토리의 main 분기에 새 GitHub Codespace를 만드는 프로세스를 시작합니다.

  2. codespace 만들기 페이지에서 codespace 구성 설정을 검토한 다음, 새 codespace 만들기를 선택합니다.

    Screenshot of the confirmation screen before creating a new codespace.

  3. codespace가 생성될 때까지 기다립니다. 이 프로세스에는 몇 분 정도 걸릴 수 있습니다.

  4. codespace에서 새 터미널을 엽니다.

    기본 메뉴를 사용하여 터미널 메뉴 옵션으로 이동한 다음, 새 터미널 옵션을 선택할 수 있습니다.

    Screenshot of the codespaces menu option to open a new terminal.

  5. Node.js가 사용자 환경에 설치되어 있는지 확인합니다.

    node --version
    

    개발 컨테이너는 v20.5.1과(와) 같은 Node.js LTS 버전을 사용합니다. 정확한 버전은 다를 수 있습니다.

  6. 이 프로젝트의 나머지 연습은 이 개발 컨테이너의 컨텍스트에서 수행됩니다.

sales.json 파일 찾기

수행할 작업은 stores 폴더에서 모든 파일을 찾는 것입니다.

stores 폴더와 이 폴더 내의 번호가 매겨진 각 하위 폴더를 확장합니다.

Screenshot that shows the project folder structure.

fs 모듈 포함

  1. ./nodejs-files 하위 폴더에서 index.js 파일을 만들어 편집기에서 엽니다.

  2. 파일 맨 위에 다음 코드를 추가하여 fs 모듈을 파일에 포함합니다.

    const fs = require("fs").promises;
    
  3. 다음으로 코드의 main 진입점인 함수를 만듭니다. 이 파일의 마지막 코드 줄은 main 메서드를 호출합니다.

    const fs = require("fs").promises;
    
    async function main() {}
    
    main();
    

    비동기 함수를 호출하는 일반적인 CommonJS 상용구 코드입니다.

파일을 찾는 함수 작성 sales.json

  1. folderName 매개 변수를 사용하는 findSalesFiles라는 새 함수를 만듭니다.

    async function findSalesFiles(folderName) {
      // FIND SALES FILES
    }
    
  2. 함수 내에서 findSalesFiles 다음 코드를 추가하여 이러한 작업을 완료합니다.

    • (1) 프로그램에서 찾은 모든 판매 파일의 경로를 보관하려면 맨 위에 배열을 추가합니다.
    • (2) 메서드를 사용하여 currentFolder를 읽습니다 readdir .
    • (3) 비동 for...of 기 루프를 사용하여 메서드에서 readdir 반환된 각 항목에 대해 루프할 블록을 추가합니다.
    • (4) 항목이 if 파일인지 디렉터리인지 확인하는 문을 추가합니다.
    • (5) 항목이 디렉터리 인 경우 재귀적으로 함수 findSalesFiles 를 다시 호출하여 항목에 대한 경로를 전달합니다.
    • (6) 디렉터리가 아닌 경우 검사 추가하여 항목 이름이 sales.json과 일치하는지 확인합니다.
    async function findSalesFiles(folderName) {
    
       // (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
       let results = [];
    
       // (2) Read the currentFolder with the `readdir` method. 
       const items = await fs.readdir(folderName, { withFileTypes: true });
    
       // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. 
       for (const item of items) {
    
         // (4) Add an `if` statement to determine if the item is a file or a directory. 
         if (item.isDirectory()) {
    
           // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. 
           const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
           results = results.concat(resultsReturned);
         } else {
           // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
           if (item.name === "sales.json") {
             results.push(`${folderName}/${item.name}`);
           }
         }
       }
    
       return results;
    }
    
  3. 이 새 findSaleFiles 함수를 main 메서드에서 호출합니다. 파일을 검색할 위치로 stores 폴더 이름을 전달합니다.

     async function main() {
       const results = await findSalesFiles("stores");
       console.log(results);
     }
    
  4. 전체 애플리케이션은 다음과 같습니다.

    const fs = require("fs").promises;
    
    async function findSalesFiles(folderName) {
    
      // (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
      let results = [];
    
      // (2) Read the currentFolder with the `readdir` method. 
      const items = await fs.readdir(folderName, { withFileTypes: true });
    
      // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. 
      for (const item of items) {
    
        // (4) Add an `if` statement to determine if the item is a file or a directory. 
        if (item.isDirectory()) {
    
          // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. 
          const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
          results = results.concat(resultsReturned);
        } else {
          // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
          if (item.name === "sales.json") {
            results.push(`${folderName}/${item.name}`);
          }
        }
      }
    
      return results;
    }
    
    async function main() {
      const results = await findSalesFiles("stores");
      console.log(results);
    }
    
    main();
    

프로그램 실행

  1. 터미널에서 다음 명령을 입력하여 프로그램을 실행합니다.

    node index.js
    
  2. 프로그램에 다음과 같은 출력이 표시됩니다.

    [
     'stores/201/sales.json',
     'stores/202/sales.json',
     'stores/203/sales.json',
     'stores/204/sales.json',
    ]
    

아주 좋습니다! 디렉터리를 트래버스하고 디렉터리에 있는 sales.json 파일을 모두 찾는 명령줄 프로그램을 작성했습니다.

그러나 이 예제에서 하위 폴더 경로를 생성한 방법은 문자열을 연결해야 하기 때문에 약간 번거롭습니다. 또한 다른 경로 구분 기호를 사용하는 운영 체제(예: Windows)에서 문제가 발생할 수도 있습니다.

다음 섹션에서는 path 모듈을 사용하여 여러 운영 체제에서 작동하는 경로를 생성하는 방법을 알아봅니다.

처리하기 어려운 부분이 있나요?

이 연습을 수행하는 동안 처리하기 어려운 부분이 있었다면 여기서 완성된 코드를 참조하세요. index.js의 모든 내용을 제거하고 이 솔루션으로 바꿉니다.

const fs = require("fs").promises;

async function findSalesFiles(folderName) {

  // (1) Add an array at the top, to hold the paths to all the sales files that the program finds.
  let results = [];

  // (2) Read the currentFolder with the `readdir` method. 
  const items = await fs.readdir(folderName, { withFileTypes: true });

  // (3) Add a block to loop over each item returned from the `readdir` method using the asynchronous `for...of` loop. 
  for (const item of items) {

    // (4) Add an `if` statement to determine if the item is a file or a directory. 
    if (item.isDirectory()) {

      // (5) If the item is a directory, recursively call the function `findSalesFiles` again, passing in the path to the item. 
      const resultsReturned = await findSalesFiles(`${folderName}/${item.name}`);
      results = results.concat(resultsReturned);
    } else {
      // (6) If it's not a directory, add a check to make sure the item name matches *sales.json*.
      if (item.name === "sales.json") {
        results.push(`${folderName}/${item.name}`);
      }
    }
  }

  return results;
}

async function main() {
  const results = await findSalesFiles("stores");
  console.log(results);
}

main();