이번 포스팅에서는 JS 엔진이 우리가 작성한 코드를 어떻게 처리하는지 깊이 있게 알아보겠습니다.

많은 개발자들이 자바스크립트를 사용하면서도 그 내부 동작 원리에 대해서는 깊이 이해하지 못하는 경우가 많습니다. 그러나 코드가 어떻게 처리되는지 이해하면 더 효율적인 코드 작성은 물론, 디버깅 능력도 크게 향상됩니다. 특히 스코프와 클로저 같은 고급 개념을 마스터하기 위한 기본 토대가 됩니다.

본 포스팅은 4번에 걸쳐서 업로드 됩니다.

  1. JS엔진은 우리가 작성한 코드를 어떻게 처리하나?
  2. 스코프는 무엇인가?
  3. 스코프의 작동 방식과 쓰임새
  4. 스코프를 다룰 때 주의할 점

 

자바스크립트와 컴파일 과정

자바스크립트 코드는 실행되기 전에 반드시 처리 과정을 거칩니다. 이 과정은 명확히 "컴파일레이션(compilation)"이라고 할 수 있습니다. 왜냐하면:

  1. 코드는 실행되기 전에 먼저 파싱됩니다
  2. 다양한 최적화가 수행됩니다
  3. 실행 가능한 형태로 변환됩니다

전통적인 컴파일 언어(C++, Java)와의 가장 큰 차이점은 자바스크립트에서는 컴파일과 실행 사이의 시간이 매우 짧다는 것입니다. 그러나 분명한 컴파일 단계가 존재합니다.

 

코드 컴파일 과정

자바스크립트 엔진이 코드를 처리하는 과정을 더 구체적으로 살펴봅시다. 이 과정은 크게 세 단계로 나뉩니다:

1. 토크나이징/렉싱(Tokenizing/Lexing)

토큰화는 문자열을 의미 있는 토큰(token)으로 나누는 과정입니다. 예를 들어:

var greeting = "안녕하세요";

위 코드는 다음과 같은 토큰으로 나뉩니다:

  • var (키워드)
  • greeting (식별자)
  • = (할당 연산자)
  • "안녕하세요" (문자열 리터럴)
  • ; (세미콜론)

이 단계에서 구문 오류가 발견되면 바로 SyntaxError가 발생합니다:

var greeting = "안녕하세요; // 닫는 따옴표 누락
// Uncaught SyntaxError: Invalid or unexpected token

2. 파싱, AST 생성

파싱 단계에서는 토큰 배열을 프로그램 문법에 맞는 중첩 구조인 AST(Abstract Syntax Tree, 추상 구문 트리)로 변환합니다.

var greeting = "안녕하세요"; 코드의 AST는 대략 다음과 같은 구조를 가집니다:

VariableDeclaration
  ├── kind: "var"
  └── declarations: [
        VariableDeclarator
          ├── id: Identifier(name: "greeting")
          └── init: Literal(value: "안녕하세요", raw: "\"안녕하세요\"")
      ]

AST Explorer(https://astexplorer.net/)와 같은 도구를 사용하면 실제 자바스크립트 코드의 AST를 시각적으로 확인할 수 있습니다. 복잡한 코드의 동작 방식을 이해하는 데 큰 도움이 됩니다.

3. 코드 생성

마지막으로, AST는 실행 가능한 코드로 변환됩니다. 이 단계에서 다음과 같은 중요한 작업이 수행됩니다:

  • 변수와 함수 선언을 처리하고 스코프와 연결
  • 코드 최적화
  • 바이트코드 또는 기계어 생성

코드 생성 단계에서는 변수나 함수 선언들의 스코프가 결정되고, 이후 실행 단계에서 활용될 준비가 됩니다.

코드 실행 과정

컴파일 과정이 완료되면 생성된 코드가 실행됩니다. 실행 단계에서는 앞서 결정된 스코프를 기반으로 변수와 함수들이 메모리에 할당되고 접근됩니다.

실행 흐름 이해하기

간단한 예제를 통해 전체 프로세스를 이해해 봅시다:

console.log(greeting);  // undefined
var greeting = "안녕하세요";
console.log(greeting);  // "안녕하세요"

function sayHello() {
  console.log("Hello!");
}
sayHello();  // "Hello!"

이 코드의 컴파일 및 실행 흐름:

  1. 컴파일 단계:
    • greeting 변수 선언 인식 및 스코프에 등록
    • sayHello 함수 선언 인식 및 스코프에 등록
    • 실행 코드 생성
  2. 실행 단계:
    • 첫 번째 console.log(greeting) 실행: greeting은 선언만 되고 아직 할당되지 않아 undefined 출력
    • greeting = "안녕하세요" 실행: 변수에 값 할당
    • 두 번째 console.log(greeting) 실행: 할당된 값 "안녕하세요" 출력
    • sayHello() 함수 호출: "Hello!" 출력

여기서 greeting이 첫 번째 로그에서 undefined로 출력되는 현상이 바로 호이스팅(hoisting)이라고 하는데, 이는 컴파일 단계에서 선언문이 먼저 처리되기 때문에 발생합니다. 호이스팅에 대해서는 다음 포스팅에서 더 자세히 다루겠습니다.

타깃과 소스의 개념

자바스크립트 엔진이 코드를 컴파일할 때 마주하는 중요한 과제 중 하나는 모든 변수와 함수의 스코프를 올바르게 결정하는 것입니다. 이 과정에서 컴파일러는 각 식별자(변수, 함수명 등)가 "타깃(target)"인지 "소스(source)"인지 판단해야 합니다.

타깃과 소스란?

  • 타깃(Target): 값이 할당되는 변수 (할당문의 왼쪽에 위치)
  • 소스(Source): 값을 제공하는 변수 (할당문의 오른쪽이나 표현식에 위치)

다음 코드에서 타깃과 소스를 식별해봅시다:

var students = [
  { id: 14, name: "카일" },
  { id: 73, name: "수지" },
  { id: 112, name: "지영" },
  { id: 6, name: "푸른" }
];

function getStudentName(studentID) {
  for (let student of students) {
    if (student.id == studentID) {
      return student.name;
    }
  }
}

var nextStudent = getStudentName(73);
console.log(nextStudent);  // "수지"

이 코드에서:

  • students는 배열 리터럴이 할당되는 타깃
  • studentID는 함수 호출 시 인자 값(73)이 할당되는 타깃
  • student는 for 루프에서 배열의 각 요소가 할당되는 타깃
  • getStudentName 함수 호출에서 73은 소스
  • nextStudent = getStudentName(73)에서 nextStudent는 타깃, 함수 호출 결과는 소스
  • console.log에서 nextStudent는 소스

실제 개발에서의 응용

타깃과 소스 개념을 이해하면 코드의 흐름과 변수 사용을 더 명확하게 파악할 수 있습니다. 예를 들어 다음과 같은 코드가 있다고 가정합시다:

var x = 10;
var y = x + 5;
var z = y * 2;

여기서 변수들의 역할을 분석하면:

  • 첫 번째 라인: x는 타깃, 10은 소스(리터럴)
  • 두 번째 라인: y는 타깃, x와 5는 소스
  • 세 번째 라인: z는 타깃, y와 2는 소스

이렇게 코드를 분석하면 데이터의 흐름을 더 명확하게 이해할 수 있습니다. 특히 복잡한 함수나 클로저를 다룰 때 이러한 분석이 도움이 됩니다.

렉시컬 스코프와 컴파일의 관계

지금까지 살펴본 자바스크립트의 컴파일 과정은 렉시컬 스코프의 개념과 직접적으로 연결됩니다. 자바스크립트에서 스코프는 컴파일 타임에 결정되며, 이를 렉시컬 스코프(어휘적 스코프)라고 합니다.

렉시컬 스코프의 핵심 원리

렉시컬 스코프의 가장 중요한 특징은 함수나 블록, 변수 선언의 스코프가 전적으로 코드의 물리적 배치에 따라 결정된다는 점입니다. 간단히 말해, 코드를 작성할 때 변수와 함수를 어디에 배치하느냐에 따라 스코프가 정해집니다.

var globalVar = "전역 변수";

function outer() {
  var outerVar = "외부 함수 변수";
  
  function inner() {
    var innerVar = "내부 함수 변수";
    console.log(globalVar);  // 전역 스코프에서 찾음
    console.log(outerVar);   // outer 함수 스코프에서 찾음
    console.log(innerVar);   // 현재 스코프에서 찾음
  }
  
  inner();
}

outer();

이 코드에서:

  • inner 함수는 자신의 렉시컬 스코프(inner 함수 내부), outer 함수의 스코프, 그리고 전역 스코프에 접근할 수 있습니다.
  • 반면, outer 함수는 inner 함수의 스코프에 접근할 수 없습니다.

컴파일 시 스코프 맵 생성

컴파일 과정에서 JS 엔진은 프로그램 전체의 스코프 구조를 담은 일종의 "지도"를 생성합니다. 이 지도는 어떤 변수가 어떤 스코프에 속하는지를 정의하며, 런타임에 변수를 찾을 때 사용됩니다.

중요한 점은 컴파일레이션 중에는 스코프를 식별하기만 하고, 실제 스코프 객체는 코드가 실행되는 런타임에 생성된다는 것입니다. 컴파일 단계에서는 스코프와 변수의 메모리 예약 관점에서 실제로는 아무것도 실행되지 않습니다.

스코프 체인과 변수 검색

변수를 참조할 때, JS 엔진은 현재 스코프에서 시작해 외부 스코프로 단계적으로 이동하며 변수를 찾습니다:

  1. 현재 스코프에서 변수명 검색
  2. 찾지 못하면 바로 바깥 스코프로 이동하여 검색
  3. 다시 찾지 못하면 또 바깥 스코프로 이동
  4. 전역 스코프까지 검색했는데도 찾지 못하면 ReferenceError 발생

이 과정은 변수가 어디에서 선언되었는지를 기준으로 한 렉시컬 스코핑이며, 코드가 어디서 호출되는지가 아니라 어디에 작성되었는지가 중요합니다.

렉시컬 스코프의 실용적 의미

렉시컬 스코프를 이해하면 코드를 더 예측 가능하게 작성할 수 있습니다. 몇 가지 실용적인 팁:

  1. 변수 선언은 사용 범위에 가장 가까운 스코프에서 하기: 전역 변수 사용을 최소화하고, 필요한 스코프에서만 변수를 선언합니다.
  2. 같은 이름의 변수 중복 선언 피하기: 특히 중첩된 스코프에서 같은 이름의 변수를 사용하면 예상치 못한 결과가 발생할 수 있습니다.
  3. 블록 스코프 활용하기: ES6의 let과 const를 사용해 변수의 스코프를 최소화합니다.
// 피해야 할 패턴
var userId = 123;

function processUser() {
  // 의도치 않게 전역 변수를 덮어씀
  userId = 456;
  // ...
}

// 권장하는 패턴
var userId = 123;

function processUser() {
  // 지역 변수로 선언하여 스코프 분리
  let userId = 456;
  // ...
}

마치며: 렉시컬 스코프와 코드 작성의 중요성

지금까지 JS 엔진이 코드를 처리하는 방식에 대해 살펴봤습니다. 자바스크립트는 컴파일 과정을 거쳐 코드를 실행하며, 이 과정에서 렉시컬 스코프가 결정됩니다.

자바스크립트에서 스코프가 컴파일 타임에 결정된다는 사실은 코드를 작성하는 방식이 매우 중요하다는 것을 의미합니다. 변수와 함수를 어디에 배치하느냐에 따라 전체 프로그램의 동작이 결정됩니다.

컴파일 중에는 스코프를 식별하기만 하고, 실제 각 스코프를 실행해야만 하는 런타임 전까지는 스코프가 생성되지 않습니다. 컴파일 단계에서는 프로그램 실행에 필요한 모든 렉시컬 스코프가 들어간 '지도'를 만들어냅니다. 이것이 런타임에 사용할 모든 코드가 들어간 계획안이라고 생각하면 됩니다.

이러한 이해를 바탕으로 코드를 작성하면:

  • 의도하지 않은 변수 참조나 충돌을 방지할 수 있습니다
  • 코드의 예측 가능성과 유지보수성이 향상됩니다
  • 스코프 관련 디버깅을 더 쉽게 할 수 있습니다

다음 포스팅에서는 "스코프는 무엇인가?"라는 주제로, 자바스크립트의 다양한 스코프 유형과 작동 방식에 대해 더 깊이 알아보겠습니다. 특히 함수 스코프와 블록 스코프의 차이, 중첩 스코프, 그리고 스코프 체인의 동작 원리를 자세히 살펴볼 예정입니다.


참고 자료:

  • "You Don't Know JS Yet" by Kyle Simpson
  • ECMA-262 명세서
  • MDN Web Docs

안녕하세요! 오늘은 JavaScript를 사용하여 달력을 구현하는 방법을 알아보겠습니다. 달력은 웹 개발에서 흔히 사용되는 UI 요소이며, 다양한 기능을 구현하는 데 활용될 수 있습니다.

1. 요일 계산

달력을 만들기 위해서는 먼저 각 달의 1일이 어느 요일인지 알아야 합니다. 이를 위해 getFirstDayIndex 함수를 정의합니다.

const getFirstDayIndex = (year, month) => {
  return (new Date(year, month, 1).getDay() + 6) % 7;
};

이 함수는 다음과 같이 작동합니다.

  1. new Date(year, month, 1): 해당 년, 월의 1일을 나타내는 Date 객체를 생성합니다.
  2. .getDay(): Date 객체의 요일을 가져옵니다. (0: 일요일, 1: 월요일, ..., 6: 토요일)
  3. + 6: 요일 값을 0부터 시작하도록 변환합니다.
  4. % 7: 7로 나눈 나머지를 계산하여 0부터 6 사이의 값을 얻습니다.

예를 들어, 2024년 3월 1일은 수요일입니다. 따라서 getFirstDayIndex(2024, 2) 함수는 3을 반환합니다.

 

2. 날짜 수 계산

다음으로 각 달의 날짜 수를 계산해야 합니다. 이를 위해 getDaysInMonth 함수를 정의합니다.

export const getDaysInMonth = (year, month) => {
  return new Date(year, month + 1, 0).getDate();
};

이 함수는 다음과 같이 작동합니다.

  1. new Date(year, month + 1, 0): 해당 년, 월의 다음 달의 0일을 나타내는 Date 객체를 생성합니다.
  2. .getDate(): Date 객체의 날짜를 가져옵니다.

예를 들어, 2024년 3월은 31일입니다. 따라서 getDaysInMonth(2024, 2) 함수는 31을 반환합니다.


달력도 고도화할 수 있는 여지가 많으며 구현 방법도 가지각색입니다. 다음 포스팅에서는 완성된 달력의 예시를 가져오면서 어떤 코드가 추가로 들어가는지 서술하겠습니다.

파이썬의 주석 처리는 크게 두가지로 나눌 수 있습니다.

1. 주석 (#)

2. 독스트링, 'docstrings', (독스트링, 특수 주석)

 1. 일반 주석 (#)

일반 주석은 주석처리를 하고 싶은 코드의 시작 지점에 # (해시 기호)를 해서 주석 처리를 할 수 있습니다.

# 기호 와 같은 행에서 뒤에 오는 모든 요소는 주석으로 간주되며 인터프리터가 # 이후의 내용을 무시하고 다음 코드줄로 이동합니다.

주석은 다양한 목적으로 사용할 수 있는데,

1. 코드 블록 혹은 변수에 대한 정보 제공
2. 코드의 복잡하거나 모호한 부분 설명
3. 함수 또는 클래스의 동작 설명
4. 인수의 목적과 예상 반환값을 문서화
5. 개발자 메모 추가
6. 테스트, 혹은 디버깅 목적으로 일부를 일시적으로 비활성화 등이 있습니다.
(예를 들어 개발을 진행하려고 했다가 이번에는 하지 못하게 되는 일에 대한 코드를 삭제하지 않고 그냥 주석으로 처리하는 경우가 존재할 수 있습니다.)

나중에 그 프로그램을 누군가가 더 발전시켜서 사용할 수도 있고,
몇달 뒤에 내가 그 프로그램을 다시 쓸 일이 있어서 레거시 코드를 열었을 때 헤멜 시간을 줄여주는 아주 소중한 존재입니다.

 2. 독스트링, 'docstrings', (독스트링, 특수 주석)

독스트링은 아래와 같은 형태를 띕니다.

"""
a is left side, b is right side
"""

이런 식으로 쌍따옴표가 세개 있는(삼중 쌍따옴표) 사이에 여러줄의 주석을 넣을 수 있습니다.

이런 여러줄 주석에 있는 변수를 활용할 수도 있다는 점이 javascript 등에서 있는 여러줄 주석과의 차이점입니다.

javascript의 여러줄 주석은 인터프리터에서 인식되지 않으며 런타임 중에 액세스 할 수 없습니다.

반면 파이썬의 독스트링은 런타임 중에 액세스 할 수 있으며 객체의 __doc__ 속성을 통해 액세스 할 수 있습니다.

독스트링은 api 문서를 자동으로 생성하기 위해 Sphinx 와 같은 문서 생성 도구에서도 사용됩니다.

/** */ 와  """ """ 둘 다 여러줄을 주석화시킬 수 있고, 함수나 파일의 윗부분에서 변수 등에 대한 설명을 해준다는 공통점이 있으나 파이썬의 독스트링은 런타임 중에 액세스 할 수 있고 문서 생성 도구에서 사용할 수 있으므로 더 강력하다는 특징을 가지고 있습니다.

데이터 처리에는 파이썬이 유용해서 파이썬을 모듈화 시켜서 이용하려고 공부 중이다.

각설하고 본론으로 넘어가서

 

집에 돌아와서 프로젝트 불러오고... 경로 확인해 준 다음에 실행을 해줬는데??

되지 않는 것이다......... 어라?? 아까 회사 컴으로 세팅할 때는 잘만 되었는데??

 

혹시나 module에 문제가 있나 싶어 이렇게 pip install을 이용해서 사용해야하는 모듈 패키지 들을 다시 다운 받아보았는데,

Warning 문구가 떴다. 

저 엑셀 파일이 문제라면 회사에서도 돌아가지 않았어야 정상인데 이 문제에 대한 원인을 찾다가...

혹시나 해서 점검해본 파이썬의 버전이 달랐다.

회사에서 생성하고 모듈 임포트를 하고 작성한 프로젝트는 3.11 버전 기반이었고,

지금 파이썬 업그레이드 전 내 로컬 pc의 파이썬 버전은 3.10이었다... 

 

혹시나 해서 챗 GPT에도 물어보았는데

 

 

이런 식으로 답변해주었다. 결국은 버전의 문제라는걸 gpt도 인지하고 있었다. 실은 gpt 덕분에 크게 시간 낭비를 하지 않았다. 익숙하지 않은 언어를 학습할 때는 챗gpt가 정말 가뭄의 단비와도 같다...!!!

지난번 포스팅에 이어서 짧은 포스팅을 추가로 올리려고 한다.

지금 하는 작업은 하드 코딩에 가깝지만, 응용한다면 데이터를 가공하는데 도움이 될 것이라 생각한다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// 학생, 학년, 학급, 학생 객체를 생성합니다.

let students = new Object();
let gradesX = new Object();
let classesX = new Object();
let studentX = new Array();

// 학생의 수만큼 k 변수로 반복문 돌립니다.
for(let k = 0; k<3; k++){
    let name = "";
    if( k%3 == 0 ){ name = "철수";
    }else if(k%3 == 1){ name = "영희";
    }else name = "동규";

    let studentIndex = {
        student_name : `${name}${k}`,
        student_height : Math.floor( (Math.random() * 120) ) + k,
        student_average : Math.floor( (Math.random() * 80) ) + k,
        student_report : 0 + k
    }
    
    studentX.push(studentIndex);
    
}  // k 변수 for문 종료

// classesX 객체에 key = student 에 value로 studentX 배열을 넣는다.
classesX.student = studentX;


// studentX 는 배열로 만들어둬서
/* classesX.student.description = "공부를 다들 열심히 합니다." */
// 와 같이 key, value를 추가할 수 없습니다.


// gradesX 객체에 key = class1 에 value로 바로 위의 classesX 객체를 넣는다. (객체 안에 객체 혹은 배열을 넣을 수 있다.)
gradesX.class1 = classesX;


// gradesX 객체에 key = class1 에 key가 teacher, 급훈인 value를 하나씩 넣는다. 우항은 string 값을 이용할 수 있어서 변수 혹은 for문 안에서도 가능하다.
gradesX.class1.teacher = "이민정쌤";
gradesX.class1.급훈 = "최선을 다하자";


// 위의 gradeX 객체를 students 객체의 key = grade1 의 value에 할당해준다.
students.grade1 = gradesX;

// 이외에 추가할만한 key, value를 할당해준다.
students.grade1.학주선생님 = "무서운 학주쌤";

// 아까 설정해둔 곳으로 접근해서 key, value 추가도 가능하다.
students.grade1.class1.특이사항 = "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다.";


// 위와 비슷하게 학년 하나를 더 가공해봅니다.
// 원래라면 학생을 request로 얻어오던지 함수를 이용해서 다르게 하는게 맞지만 test를 위한 과정이라 같은 더미데이터를 사용하겠습니다.
let class2 = new Object();
class2 = classesX;
class2.teacher = "배용준쌤";
class2.급훈 = "잘생긴게 최고다";
students.grade1.class2 = class2;
students.description = "오지고 학생들";

console.log( JSON.stringify( students ) );

 

결과는 다음과 같다

{
  "grade1": {
    "class1": {
      "student": [
        {
          "student_name": "철수0",
          "student_height": 19,
          "student_average": 63,
          "student_report": 0
        },
        {
          "student_name": "영희1",
          "student_height": 57,
          "student_average": 25,
          "student_report": 1
        },
        {
          "student_name": "동규2",
          "student_height": 13,
          "student_average": 77,
          "student_report": 2
        }
      ],
      "teacher": "배용준쌤",
      "급훈": "잘생긴게 최고다",
      "특이사항": "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다."
    },
    "학주선생님": "무서운 학주쌤",
    "class2": {
      "student": [
        {
          "student_name": "철수0",
          "student_height": 19,
          "student_average": 63,
          "student_report": 0
        },
        {
          "student_name": "영희1",
          "student_height": 57,
          "student_average": 25,
          "student_report": 1
        },
        {
          "student_name": "동규2",
          "student_height": 13,
          "student_average": 77,
          "student_report": 2
        }
      ],
      "teacher": "배용준쌤",
      "급훈": "잘생긴게 최고다",
      "특이사항": "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다."
    }
  },
  "description": "오지고 학생들"
}

 

웹에서 데이터를 받고 전송을 한다면 json 형식과는 뗄레야 뗄 수가 없을 것이다.

지금까지는 간단한 형식의 json 파일만 사용해서 잘 몰랐던 점들이 있었는데, 오늘 연습해보고 기록하면서 json에 좀 더 잘 알 수 있기를 !!!

 

 배열만 이용한 json 만들기 연습

 

보통 이 경우는 이중 혹은 삼중, 그 이상의 배열을 다룰 때 쉽게 활용할 수 있다.

기본적으로 testArr[ i ][ j ][ k ] 이런 식으로 활용이 될 것이다.

예제 코드는 다음과 같다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// Json 형식이 될 빈 리스트 생성
const student = new Array();

// 학년만큼 i 변수를 반복문 돌립니다.
for( let i = 0; i< 2; i++){
    // 학년 배열 생성
    let grades = new Array() ;
    
    // 학급의 수 만큼 j 변수를 반복문 돌립니다.
    for(let j = 0; j<4; j++){
        // 학급 배열 생성
        let classes = new Array() ;

        // 학생의 수만큼 k 변수로 반복문 돌립니다.
        for(let k = 0; k<3; k++){
            // 학생 객체 생성
            let students = new Object() ;
            if( k%3 == 0 ){
                students.student_name = `철수${i}${j}${k}`;
                students.student_height = Math.floor( (Math.random() * 120) ) + k;
                students.student_average = Math.floor( (Math.random() * 80) ) + k;
                students.student_report = 0 + k;
            }else if(k%3 == 1){
                students.student_name = `영희${i}${j}${k}`;
                students.student_height = Math.floor( (Math.random() * 120) ) + k;
                students.student_average = Math.floor( (Math.random() * 80) ) + k;
                students.student_report = 0 + k;
            }else{
                students.student_name = `동규${i}${j}${k}`;
                students.student_height = Math.floor( (Math.random() * 120) ) + k;
                students.student_average = Math.floor( (Math.random() * 80) ) + k;
                students.student_report = 0 + k;
            }
            
            classes.push(students);
        }
        grades.push(classes);
    }
    student.push(grades);
}

// 전체 학생
console.log("전체 학생");
console.log( JSON.stringify( student ) );

// 한 학년의 학생
console.log("한 학년의 학생");
console.log( JSON.stringify( student[0] ) );

// 한 반의 학생
console.log("한 반의 학생");
console.log( JSON.stringify( student[0][0] ) );

// 한명의 학생
console.log("한명의 학생");
console.log( JSON.stringify( student[0][0][0] ) );

 

결과

전체 학생
student_make_json.js:44
[[[{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0},{"student_name":"영희001","student_height":72,"student_average":9,"student_report":1},{"student_name":"동규002","student_height":119,"student_average":62,"student_report":2}],[{"student_name":"철수010","student_height":101,"student_average":10,"student_report":0},{"student_name":"영희011","student_height":81,"student_average":67,"student_report":1},{"student_name":"동규012","student_height":114,"student_average":43,"student_report":2}],[{"student_name":"철수020","student_height":17,"student_average":75,"student_report":0},{"student_name":"영희021","student_height":14,"student_average":75,"student_report":1},{"student_name":"동규022","student_height":111,"student_average":39,"student_report":2}],[{"student_name":"철수030","student_height":59,"student_average":44,"student_report":0},{"student_name":"영희031","student_height":59,"student_average":45,"student_report":1},{"student_name":"동규032","student_height":96,"student_average":25,"student_report":2}]],[[{"student_name":"철수100","student_height":104,"student_average":28,"student_report":0},{"student_name":"영희101","student_height":87,"student_average":70,"student_report":1},{"student_name":"동규102","student_height":39,"student_average":3,"student_report":2}],[{"student_name":"철수110","student_height":88,"student_average":47,"student_report":0},{"student_name":"영희111","student_height":42,"student_average":23,"student_report":1},{"student_name":"동규112","student_height":44,"student_average":6,"student_report":2}],[{"student_name":"철수120","student_height":102,"student_average":19,"student_report":0},{"student_name":"영희121","student_height":119,"student_average":41,"student_report":1},{"student_name":"동규122","student_height":83,"student_average":49,"student_report":2}],[{"student_name":"철수130","student_height":11,"student_average":15,"student_report":0},{"student_name":"영희131","student_height":97,"student_average":46,"student_report":1},{"student_name":"동규132","student_height":121,"student_average":71,"student_report":2}]]]
student_make_json.js:45
한 학년의 학생
student_make_json.js:48
[[{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0},{"student_name":"영희001","student_height":72,"student_average":9,"student_report":1},{"student_name":"동규002","student_height":119,"student_average":62,"student_report":2}],[{"student_name":"철수010","student_height":101,"student_average":10,"student_report":0},{"student_name":"영희011","student_height":81,"student_average":67,"student_report":1},{"student_name":"동규012","student_height":114,"student_average":43,"student_report":2}],[{"student_name":"철수020","student_height":17,"student_average":75,"student_report":0},{"student_name":"영희021","student_height":14,"student_average":75,"student_report":1},{"student_name":"동규022","student_height":111,"student_average":39,"student_report":2}],[{"student_name":"철수030","student_height":59,"student_average":44,"student_report":0},{"student_name":"영희031","student_height":59,"student_average":45,"student_report":1},{"student_name":"동규032","student_height":96,"student_average":25,"student_report":2}]]
student_make_json.js:49
한 반의 학생
student_make_json.js:52
[{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0},{"student_name":"영희001","student_height":72,"student_average":9,"student_report":1},{"student_name":"동규002","student_height":119,"student_average":62,"student_report":2}]
student_make_json.js:53
한명의 학생
student_make_json.js:56
{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0}

 

기왕에 연습겸 만들어본거 헷갈리던 개념도 한번 시험해보았다.

Js에서 for of과 for in 구문의 차이점을 실제로 체감하기가 힘들었는데 오늘의 예제가 도움이 되었다.

 

// 1학년 1반의 모든 학생 출력
console.log("1학년 1반의 모든 학생 출력");
for( let i = 0; i < student[0][0].length; i++ ){
    console.log( JSON.stringify( student[0][0][i] ) );
}

/** 결과
{"student_name":"철수000","student_height":49,"student_average":13,"student_report":0}
student_make_json.js:63
{"student_name":"영희001","student_height":97,"student_average":77,"student_report":1}
student_make_json.js:63
{"student_name":"동규002","student_height":88,"student_average":21,"student_report":2}
*/



// student[0][0] 을 변수화 시킨 후 1학년 1반의 모든 학생 출력
console.log("student[0][0] 을 변수화 시킨 후 1학년 1반의 모든 학생 출력");
let temp = student[0][0];
for( let i = 0; i < temp.length; i++ ){
    console.log( JSON.stringify( temp[i] ) );
}

/** 결과
{"student_name":"철수000","student_height":49,"student_average":13,"student_report":0}
student_make_json.js:70
{"student_name":"영희001","student_height":97,"student_average":77,"student_report":1}
student_make_json.js:70
{"student_name":"동규002","student_height":88,"student_average":21,"student_report":2}
*/



// 반복문을 출력하는 여러가지 방법 중 하나, for of 구문
console.log("반복문을 출력하는 여러가지 방법 중 하나, for of 구문");
temp = student[0][0];
for( let item of temp ){
    console.log( JSON.stringify( item ) );
    console.log( item );
}

/** 결과
{"student_name":"철수000","student_height":49,"student_average":13,"student_report":0}
student_make_json.js:77
{student_name: '철수000', student_height: 49, student_average: 13, student_report: 0}
{"student_name":"영희001","student_height":97,"student_average":77,"student_report":1}
student_make_json.js:77
{student_name: '영희001', student_height: 97, student_average: 77, student_report: 1}
{"student_name":"동규002","student_height":88,"student_average":21,"student_report":2}
student_make_json.js:77
{student_name: '동규002', student_height: 88, student_average: 21, student_report: 2}
*/



// 반복문을 출력하는 여러가지 방법 중 하나, for in 구문
console.log("반복문을 출력하는 여러가지 방법 중 하나, for in 구문");
temp = student[0][0];
for( let item in temp ){
    console.log( JSON.stringify( item ) );
    console.log( item );
}

/** 결과
"0"
student_make_json.js:85
0
student_make_json.js:86
"1"
student_make_json.js:85
1
student_make_json.js:86
"2"
student_make_json.js:85
2
*/

 

이 외에도 맨 위에 동일한 작업이 반복되는 코드를 함수화 시켜서 할 수도 있습니다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// Json 형식이 될 빈 리스트 생성
const student = new Array();

let makeStudentObject=( students, i, j, k )=>{
    let name = "";
    if( k%3 == 0 ){ name = "철수";
    }else if(k%3 == 1){ name = "영희";
    }else name = "동규";
    students.student_name = `${name}${i}${j}${k}`;
    students.student_height = Math.floor( (Math.random() * 120) ) + k;
    students.student_average = Math.floor( (Math.random() * 80) ) + k;
    students.student_report = 0 + k;
    return students;
}

// 학년만큼 i 변수를 반복문 돌립니다.
for( let i = 0; i< 2; i++){
    // 학년 배열 생성
    let grades = new Array() ;
    
    // 학급의 수 만큼 j 변수를 반복문 돌립니다.
    for(let j = 0; j<4; j++){
        // 학급 배열 생성
        let classes = new Array() ;

        // 학생의 수만큼 k 변수로 반복문 돌립니다.
        for(let k = 0; k<3; k++){
            classes.push( makeStudentObject( new Object() , i, j, k ));
        }
        grades.push(classes);
    }
    student.push(grades);
}

/** 결과
[[[{"student_name":"철수000","student_height":85,"student_average":79,"student_report":0},{"student_name":"영희001","student_height":46,"student_average":34,"student_report":1},{"student_name":"동규002","student_height":47,"student_average":18,"student_report":2}],[{"student_name":"철수010","student_height":118,"student_average":13,"student_report":0},{"student_name":"영희011","student_height":33,"student_average":28,"student_report":1},{"student_name":"동규012","student_height":95,"student_average":13,"student_report":2}],[{"student_name":"철수020","student_height":53,"student_average":6,"student_report":0},{"student_name":"영희021","student_height":26,"student_average":80,"student_report":1},{"student_name":"동규022","student_height":112,"student_average":59,"student_report":2}],[{"student_name":"철수030","student_height":71,"student_average":64,"student_report":0},{"student_name":"영희031","student_height":69,"student_average":23,"student_report":1},{"student_name":"동규032","student_height":74,"student_average":24,"student_report":2}]],[[{"student_name":"철수100","student_height":47,"student_average":30,"student_report":0},{"student_name":"영희101","student_height":36,"student_average":66,"student_report":1},{"student_name":"동규102","student_height":109,"student_average":21,"student_report":2}],[{"student_name":"철수110","student_height":39,"student_average":71,"student_report":0},{"student_name":"영희111","student_height":104,"student_average":17,"student_report":1},{"student_name":"동규112","student_height":102,"student_average":41,"student_report":2}],[{"student_name":"철수120","student_height":57,"student_average":29,"student_report":0},{"student_name":"영희121","student_height":22,"student_average":15,"student_report":1},{"student_name":"동규122","student_height":35,"student_average":15,"student_report":2}],[{"student_name":"철수130","student_height":30,"student_average":72,"student_report":0},{"student_name":"영희131","student_height":111,"student_average":41,"student_report":1},{"student_name":"동규132","student_height":105,"student_average":70,"student_report":2}]]]
*/

 

오늘은 json 을 활용해서 해볼 수 있는 간단한 활용과 예제에 대해서 알아보았습니다.

https://codebeautify.org/jsonviewer

 

Best JSON Viewer and JSON Beautifier Online

Online JSON Viewer, JSON Beautifier and Formatter to beautify and tree view of JSON data - It works as JSON Pretty Print to pretty print JSON data.

codebeautify.org

와 같은 사이트에서 json을 보기 좋게 변환해서 볼 수도 있고,

이외에도 json prettifier 라고 구글에 치시면 비슷한 역할을 하는 웹사이트가 이미 많이 서비스 되고 있습니다.

이와 같이 json 파일이 현재 어떻게 되어 있는지 확인할 수 있습니다.

 

반복문으로 배열을 생성하고 json 화 시키려고 할 때 key에 string 값을 줄 수가 없어서 

명시적으로 하다보면 하드코딩이 되어버리곤 합니다.

이번에 사용하듯이 그냥 배열만 가지고 이용하면서 index 값을 통해 운영한다면 훨씬 쾌적하고 빠르게 코딩할 수 있습니다.

+ Recent posts