본문 바로가기
에러모음/자바스크립트

[개행 문자] 읽어온 json 파일과 js 문자열이 같은 데도 계속 다른 경우

by 야옹아옹 2022. 11. 23.

페어 과제 진행 중에 user1.json 파일을 readFile로 읽어서 가져온 후,

가져온 data와 테스트 코드에 있는 값이 눈으로 보기에는 똑같았는데 계속 다르다는 오류가 발생했다.

오류 해결 과정과 원인을 블로그로 남긴다.

 

✨ 그래서 어떤 오류냐면...

const user1txt = `{
  "name": "김코딩",
  "age": 26,
  "sex": "Male",
  "company": {
    "name": "코드스테이츠"
  }
}`;
test("callback 두번째 인자에 파일 내용이 전달되어야 합니다", (done) => {
      getDataFromFile(jsonPath, (err, data) => {
        expect(err).toBeNull();
        expect(data).toBe(user1txt);
        done();
      });
});

분명히 같은 문자열인데 위 테스트 코드를 계속 통과하지 못하는 문제에 직면했다.

계속 다르다는데요?

초록과 빨강.. 육안으로 보면 똑같은데 왜 다르다는거니?

그래서 두 문자열의 길이를 비교해봤다. 그런데 문자열의 길이가 다르다!

97과 90...? data가 7개나 더 많다

이게 어떻게 된 걸까?

눈으로 보면 똑같은데 왜 내가 readFile로 읽어온 json파일 데이터 7글자가 더 많은 걸까?

순수하게 도대체 무슨 문자가 있는 건지 궁금해서 for문을 돌려서 data를 한글자씩 확인해 보니 뭔가 빈 공간 같은 문자가 user1txt보다 많이 찍혔다. 사진은 첨부하지않겠다….

아무리봐도 이스케이프 문자가 더 들어간 것 같은데...

이스케이프 문자란 \로 시작하는 이스케이프 시퀀스를 따르는 문자다.

대표적으로 \n 줄바꿈 문자로 많이 봤을 것이다.

위의 글자수가 다른 점, for문에서 뭔가 빈 문자가 찍히는 것 같았기 때문에 이스케이프가 들어가있다고 판단했다.

그럼 이스케이프 문자는 어떻게 확인할까?

JSON.stringfy()를 사용해 JSON문자열로 변환하면 이스케이프를 확인할 수 있다.

그냥 콘솔로 찍으면 이스케이프 문자가 보이지 않는다. 따라서 이를 JSON.stringfy 해준 값을 콘솔에 찍으면 이스케이프 문자를 볼 수있다.

data와 user1txt를 JSON.stringfy 해보면 아래처럼 나온다.

data에는 \r이 더 붙어있다!

일단 \r이 무엇이고 \r\n이 무엇인지는 나중에 설명하겠다. 일단 이 \r때문에 두 문자열이 다르다고 인식되는 것 같기 때문에 \r을 제거해주기로 했다.

 

일단 해결해보자!

올바른 비교를 위해 \r 제거해주기

stringfy된 data에서 \r을 모두 찾아서 ""로 대체하는 방식으로 제거하였다.

const stringfied = JSON.stringify(data).replaceAll("\\r", "");

JSON 문자열을 다시 자바스크립트에서 사용하는 값으로 만들기

\r이 제거된 문자열의 형태는 JSON 문자열 형태이다.

이렇게 \r가 제거된 JSON 형식의 문자열을 다시 JS에서 사용하는 값으로 바꿔줘야한다. 이럴때 사용하는 메서드가 JSON.parse()이다.

const parsedData = JSON.parse(stringfied)

다시 테스트해보기

기존에 그냥 data를 넘겨주었다면 이제 \r을 제거한 데이터를 넘겨주면 된다.

전체 코드는 아래 더보기 버튼 클릭

더보기

전체 코드

const getDataFromFile = function (filePath, callback) {
  // TODO: fs.readFile을 이용해 작성합니다

  const options = {
    encoding: "utf8", // utf8 인코딩
  };
  fs.readFile(filePath, options, (err, data) => {
    if (err) {
      callback(err, null);
    } else {
      // 이렇게 가져오면 data에 \r\n 이렇게 개행문자가 들어감
      // 그런데 비교하는 테스트 객체에는 \r가 없음
      // 따라서 stringfy를 해준다음에 \r를 전부 제거
      const stringfied = JSON.stringify(data).replaceAll("\\r", "");
      callback(null, JSON.parse(stringfied));
    }
  });
};

성공적으로 테스트를 통과한다!

그렇다면 이제 왜 내가 읽은 json 파일에는 \r\n이 붙어있는지 근본적인 원인을 알아볼 차례이다.

 

 

그래서 \r\n이 왜 붙어있던건가요?

이는 개행 문자를 처리하는 방법 때문에 일어난 일이다.
개행문자란?
텍스트의 한 줄이 끝남을 표시하는 문자 또는 문자열을 말한다.
= 줄바꿈 문자 line break = EOL(End-Of-Line) 

윈도우는 개행 문자로 \r\n을 쓰고 맥과 리눅스는 OS는 \n을 사용한다.

그렇다. 윈도우CRLF(\r\n)방식을 사용하고 맥과 리눅스LF(\n)방식을 사용한다. 

  • 나는 윈도우를 사용하기 때문에 user1.json파일이 CRLF방식으로 저장이 되어있었고 해당 파일을 읽어오니 개행문자는 CRLF 방식을 사용되어 \r\n가 개행마다 붙어있었다.
  • 그러나 비교하는 문자열은 개행이 LF(\n)방식으로 되어있었기 때문에 두 문자열이 눈으로 보기에 똑같지만 절대로 같다고 나오지 않던 것이다.

CRLF란?

CR 그리고 LF는 제어문자이다. 주로 텍스트파일의 라인 마지막 line break를 표시할때 사용한다.
- 컴퓨터는 새로운 줄을 바꾸는 방식(개행)으로 \r과 \n을 사용한다.
  • CR = Carriage Return \r
    • cursor를 라인의 시작부분으로 옮긴다.
  • LF = Line Feed \n
    • cursor를 다음 줄로 이동한다.
  • CRLF = \r\n
    • cursor를 다음 줄로 이동한 다음 다음 줄의 시작으로 이동한다.

번외) VSCode로 문제 간단하게 해결하기

VSCode의 오른쪽 하단을 보면 개행 문자 방식을 변경할 수 있다.

변경하고자 하는 파일로 이동 후, 개행 문자 방식을 변경해 다시 저장하면 해당 파일의 개행 문자 방식이 변경된다.

지저분한...검은칠은 흐린눈 해주세요.

이렇게 읽어오는 파일 자체를 LF로 변경하면, \r가 들어있지 않기때문에 JSON.stringfy나 replaceAll을 해줄 필요 없이 그냥 바로 data를 콜백함수에 넘겨주면된다.

 

후기

EOL, End-Of-Line은 당연히 들어봤다. 그런데 OS별로 개행문자를 처리하는 방법이 다른 것이라고 생각도 못했고 이로인해 문제가 발생하는 건 더더욱 생각해보지 못했다.

공부를 진행하면서 내가 가진 CS 지식이 얼마나 부족한지 크게 느끼고 있다. 그래서 더욱 기초부터 다시 공부를 시작한 것에 대해 후회가 없다.

 

 

 

댓글