---
# System prepended metadata

title: 강남_M_Day13 리뷰 레포트

---

# 강남_M_Day13 리뷰 레포트

### 참석자
- 나영균, 우연서, 이상아, 한혜경

## 1. 코드 동작 이해
```
- 가장 많은 체크포인트를 구현한 사람의 코드의 
동작 원리를 이해하는 과정에서 발생한 건설적인 대화를 기록, 정리합니다.
```
- 연서님 코드
    - 정규식을 이용해서 String을 토크나이징한다
    - 토크나이징 한 배열을 각 타입을 맞게 객체로 변환한다
    - 객체를 그래프로 만들고 재귀로 미션에서 요구하는 형식의 객체로 파싱한다.

- 영균님 코드
    - tokenizer에서 정규식을 이용하여 separator를 살려서 토크나이징 한다.
    - tokenizer에서 문자열 검사와 브라켓 검사를 하고 에러시 에러를 발생시킨다.
    - lexer에서 tokenizer결과를 typeMatching을 한다.
    - parser에서 브라켓들을 감지하여 1차원에 속하는 원소들은 그대로 mainArr에 넣고 브라켓이 열려서 2차원 이상의 원소들과 브라켓은 subArr에 넣는다.
    - 브라켓이 닫히면 subArr을 그대로 mainArr에 넣는다.
    - 위 작업이 끝나면 mainArr을 반복문으로 돌며 array인 경우 parser로 재귀하여 결과를 array와 치환한다.

## 2. 코드 동작 개선
```
- 다함께 하나 이상의 체크포인트를 구현하기 위해 시도했던 문제 해결의 과정을 정리합니다.
- 학습정리의 `스스로 확인할 사항`에 대해 고민한 내용을 공유하고 
서로의 코드를 더 발전시킬 수 있는 형태로 내용을 기록합니다.
```
### 나영균
- 재귀를 이용하여 구현하였는데 이를 스택과 반복문으로도 구현해보는 것이 좋겠다.
    - 매우매우 큰 구조의 경우 재귀로 실행할 시 stackoverflow가 발생할 것이다.
- tokenizer에서 필요없는 요소를 처리하는 부분을 조금 더 중복되지 않고 효율적으로 구현한다면 좋을 것이다.
- 문자열안에 들어오는 특수문자들과 브라켓의 예외상황을 처리하였지만 Array의 상황이외에도 더 많은 상황에 대하여 예외를 처리한다면 좋을것이다.

### 우연서
* 트리 구성 후 객체로 변환하고 있지만 토큰의 값을 깔끔하게 정리하고 두번에 걸친 파싱 작업을 한번으로 줄일 수 있을 것 같다.
* 부모노드(현재 작업하고 있는 배열의 위치)를 어떤 방식으로 접근해야할지에 대한 방법으로 연결리스트를 선택했는데 배열의 스택으로도 부모노드를 기억할 수 있다.
* 토큰의 경우들을 if 문으로 확인하였는데, object로 분리하여 상수처리해서 추가 제거가 용이하게 할 수 있을 것 같다.

### 이상아
* 재귀로 작성하였을 때, object의 자식의 자식의 자식 ... 을 탐색하는 것은 구현되었지만, 다시 나오는 것을 구현하지 못하였다.
* for문 으로는 과제 제출시간 이후 구현하였지만, 재귀로 구현할 수 있었다면 더 좋았을 것 같다.

### 한혜경
- 현재 코드는 일차 배열에 대한 문자열만 분석한다. 재귀나 반복문을 사용하여 전체를 다 분석했어야 했는데 나중에 문제를 찾아서 고치기 힘들었다. tokenizer에서부터 작은 단위로 쪼개는 게 문제가 됐는데 "Hello, world"와 같은 ',' 구분자로 분류하는 방법을 찾다가 시간만 많이 쓰고 망했다. 일단 진행하고 이런 문제는 나중에 찾자. 그리고 여러 함수를 구현해야 할 때 이해가 되는 함수부터 구현하면 타고 올라가면서 좀 더 수월하게 구현할 수 있었을 것 같다.

### 스스로 확인할 사항
### 나영균
#### 무한 중첩된 구조를 프로그래밍 하는데 효과적인 방법은 무엇인가? 재귀인가, 반복인가?
- **Recursion**
    - 재귀를 통하여 프로그래밍을 하면 직관적으로 짤 수 있다.
    - 부수효과를 줄일 수 있다.
    - 재귀가 더 느린 이유는 함수콜이 모두다 스택에 쌓여야하기 때문이다.
        - 또한 메모리도 할당해야하는데 그 이유는 분리된 스코프를 유지해야 하기 때문이다.
    - 몇몇의 경우(Tree같은)에는 recursion이 가독성측면에서 좋은 경우가 있다.
- **Iteration**
    - 성능적 효율이 더 중요한 요소라면 반복을 통해 처리하는 것이 좋다.
    - 재귀는 stackoverflow가 발생할 수 있다.
> 실제로 recursion은 CPU의 내부적으로 Iteration처럼 작동하게 된다.
> 적재적소에 사용하는것이 좋다고 생각한다.
#### token, lexer, parser 역할을 나눠서 단계적으로 처리하고 있는가?
- **tokenizer**
    - ' , [ ]를 기준으로 문자열을 분리하고 따옴표와 브라켓이 올바르게 있는지 확인합니다.
    - 또한 필요없는 원소인 ,와 ""와 " "를 제거합니다.
    - 처리된 배열을 리턴합니다.
- **lexer**
    - tokenizer가 리턴한 배열을 인자로 받습니다.
    - typeAttacher로 매칭이 되는 type들을 tagging합니다.
    - 태깅한 배열을 리턴합니다.
- **parser**
    - lexer가 리턴한 배열을 입력으로 받습니다.
    - 각각의 원소들을 분석하여 얼마다 깊은 차원으로 원소를 넣어야하는지 판단하고 더 깊은 차원의 배열은 재귀로 호출하여 처리합니다.
    - 최종 Object를 반환합니다.
#### 에러처리에 대한 흐름제어를 할 수 있는가?
- tokenizer, lexer, parser의 단계에서도 많은 단계로 모듈화하여 적용하여 배열의 에러와 문자열의 에러를 따로 catch할 수 있으며 원한다면 더 세부적인 에러처리를 구현할 수 있습니다. 이는 소규모 모듈로 분리한 설계의 장점입니다.

### 우연서
#### 무한 중첩된 구조를 프로그래밍 하는데 효과적인 방법은 무엇인가? 재귀인가, 반복인가?
 - 재귀의 경우 함수가 여러번 할당되기 때문에 메모리의 비효율이 발생할 수 있고 무한루프에 빠질 수 있다는 단점이 있다. 하지만 개발자가 작업에 용이하고 DFS의 경우 훨씬 단순하게 작업할 수 있다는 장점이 있다.
 - 반복의 경우에는 메모리 사용에 이점이 있지만 각 단계에서 체크해야 되는 것들이 있어서 개발에서 확인해야할 것들이 있다.
#### token, lexer, parser 역할을 나눠서 단계적으로 처리하고 있는가? 
 - 토큰을 나누고 각 단위에 맞게 값을 부여한 다음 파서에서 트리를 만드는 단계로 구성하고 있다. 단계별로 배열을 받고 리턴함으로써 테스트 코드로 각 함수의 테스트가 가능하다. 
#### 에러처리에 대한 흐름제어를 할 수 있는가?
 - 커스텀 익셉션을 만들어서 인자오류를 출력하고 있다. 파싱을 실행하는 함수에서 catch를 하기 때문에 하위 함수에서 에러가 발생해도 상위 함수에서 에러처리가 가능하다. 
### 이상아
#### 무한 중첩된 구조를 프로그래밍 하는데 효과적인 방법은 무엇인가? 재귀인가, 반복인가?
- **코드의 간결함과 가독성** 면에서는 재귀가 반복문보다 낫다.
- 하지만 **성능** 면에서는 반복문이 재귀보다 낫다. 왜냐하면 재귀는 자신을 호출하고, 그 안에서 또 자신을 호출하고 ... 이것을 반복하기 때문이다. 이 과정에서 재귀 호출이 일정 횟수 이상을 넘어가면 알고리즘 실행 속도 또한 저하될 수 있다.
- 각각의 단점을 개선하는 **꼬리재귀**라는 것이 있다.
    - 코드를 보자
    ```javascript
    // 꼬리재귀가 아니다
    int foo ( int n )
    {
        if ( n == 0 )
        {
            return 1;
        }
        return 2 * foo ( n - 1 );
    }
    
    // 꼬리재귀 이다
    int gcd ( int m, int n )
    {
        if ( n == 0 )
        {
            return m;
        }

        else {
            return gcd ( n, m%n );
        }
    }
    ```
    * 꼬리재귀의 경우 호출될 때 리턴되는 위치를 스택에 저장하지 않는다.
        
- [출처 - 티스토리 블로그](https://makefortune2.tistory.com/98)
#### token, lexer, parser 역할을 나눠서 단계적으로 처리하고 있는가?
- token, lexer는 단계적으로 올바르게 처리하지만 parser는 제대로 처리하지 못한다.
#### 에러처리에 대한 흐름제어를 할 수 있는가?
- 구현하지 못하였다.
### 한혜경
#### 무한 중첩된 구조를 프로그래밍 하는데 효과적인 방법은 무엇인가? 재귀인가, 반복인가?
- 시각적으로 보여주기에는 재귀함수를 이용하는 게 좋지만, 성능 측정을 위해 십만번, 백만번 반복하지 않아도 실용적인 몇 십번, 몇 백번 단위에서도 확연하게 성능 차이가 많이 나기 때문에 반복문을 사용하는 것이 더 낫다고 생각한다.

#### token, lexer, parser 역할을 나눠서 단계적으로 처리하고 있는가?
- token: 문자열을 받아 각각의 요소를 최소 단위로 분리해준다. 다만 내가 짠 코드는 안에 있는 요소를 모두 분리하지 못했다.
- lexer: token으로 쪼개진 단위들 각각의 요소에 type과 value를 부여한다. 
- parser: 각각의 요소를 분석하여 부모 자식 관계를 보여준다.

#### 에러처리에 대한 흐름제어를 할 수 있는가?
- 문자열에서 '[]'로 묶이지 않거나 '"', "'" 와 같은 문자열이 한 쌍으로 묶여있지 않을 때 체크하기 위해 
```js
match = {
    '[': 0,
    ']': 0,
    "'": 0,
    '"': 0
}
```
를 이용하여 모든 문자열이 끝났을 때 각각의 개수가 같은지 확인하고 맞지 않으면 에러를 발생시켰다.(1차 배열일 때 적용됨)

## 3. Consideration
```
- 학습정리의 `다같이 확인할 사항`에 대해 충분히 토의하고 
그 과정을 일목요연하게 정리합니다.
```
### 다같이 확인할 사항

#### 소프트웨어 개발에서, token, lexer, parser의 역할은 각각 무엇인지 조사해보자.
- token
    - 컴파일러에서의 토큰에서 해당하는 단위(명칭,상수,지정어,연산자,구분자 등)로 분할한다.
    - lexical analysis 결과로서 의미가 있는 최소 문자단위를 의미한다.
- lexer : 어휘분석
    - 컴퓨터과학에서 lexical analysis(어휘 분석)은 문자열을 sequence of tokens(의미가 첨부된 문자열)로 바꾸는 프로세스를 의미한다. Lexical analysis를 실행하는 프로그램은 lexer, tokenizer, 혹은 scanner라고 불린다(scanner는 lexer의 첫 단계를 의미하기도 한다. tokenizer와 lexer가 헷갈리는 이유일 것이다.). lexer는 일반적으로 parser와 함께 묶여있는 경우가 많다.

- parser : 구문분석
    - 컴파일러에서 생선된 토큰을 렉싱한 것을 파스 트리를 구성하는 것을 파싱이라고 하는데. 컴파일러에서의 파싱 방법에서는 상향파싱, 하양파싱 등이있다.
    - 파싱에 단계 토큰이 생성 가능한 것인지를 판별하고 구문을 분석한다.


#### 이번 미션에서 구현한, token, lexer, parser의 각각 역할과 동작방식을 요약해서 설명해보자.

-  data: `['hi', 2, 3, [4, 5, [6, 7], 'h']]`
-  tokenizer 는 **의미있는 단위로 쪼개는 역할**.

    -  `[`, `hi`, `2`, `[`, `4`, `5`, `6`, `7`, `]`, `'h'`, `]`, `]` 
-  laxer는 **각각의 쪼개진 데이터에 의미를 부여**. 
    ```javascript
     [{ type: 'LBracket', value: '[' },
      { type: 'string', value: '\'hi\'' },
      { type: 'number', value: 2 },
      { type: 'number', value: 3 },
      { type: 'LBracket', value: '[' },
      { type: 'number', value: 4 },
      { type: 'number', value: 5 },
      { type: 'LBracket', value: '[' },
      { type: 'number', value: 6 },
      { type: 'number', value: 7 },
      { type: 'RBracket', value: ']' },
      { type: 'string', value: '\'h\'' },
      { type: 'RBracket', value: ']' },
      { type: 'RBracket', value: ']' } ]
     ```
-  parser는 **의미를 부여한 데이터를 구조화**.
    ```javascript
    {
      type: "array",
      child: [
        { type: "string", value: "'hi'" },
        { type: "number", value: 2 },
        { type: "number", value: 3 },
        {
          type: "array",
          child: [
            { type: "number", value: 4 },
            { type: "number", value: 5 },
            {
              type: "array",
              child: [
                  { type: "number", value: 6 }, 
                  { type: "number", value: 7 }]
            },
            { type: "string", value: "'h'" }
          ]
        }
      ]
    };
    ```
    