최근 논문 읽기와 관련한 포스트를 연달아 번역했다. 모두 papers we love에서 언급된 포스트인데 논문을 어떻게 읽기 시작하면 좋을지 고민하는 사람에게 좋은 지침이 될 것 같아 번역해 공유했다.

내가 논문을 심각하게 읽어본 적이 있나 생각해보면 대학 3학기 경험 중 과제를 준비하며 관련 논문을 뒤적인 것이 전부였다. 주제가 지금도 생각나는데 초등학교 재학생수 변화 추이와 배후지역의 인구밀도 변화에 관한 내용이였다. 지금 생각해보면 좀 더 적극적으로 조언도 받고 논문도 부지런히 읽었어야 했다. 학과 교수님도 논술에 대비해 학회지를 읽을 것을 권했는데(지금은 모르겠는데 임용고사에 논술이 있었다.) 그리고 학과 사무실 앞 책상에 놓여있는 책을 대충 훑어봤던 기억만 난다. 어떻게 읽어야 하는가에 관한 번역하면서, 만약 이런 글을 당시에 읽었다면 더 적극적으로 (아마도 재미있게) 공부할 수 있지 않았을까 싶었다. 단순히 학부생이라서 흥미가 없었던 점보다는 무엇을 어떻게 읽어야 하는지 가이드도 전혀 없었던 그 막막함이 더 컸던 것 같다.

논문은 정제된 아이디어를 구조화된 양식으로 적어놓은 결과물이다. 논문을 읽는 일을 전업으로 삼는 경우가 아니더라도 논문을 읽는 과정에서 새로운 기술을 습득하는 것은 물론, 체계적으로 아이디어를 정리하는 방법, 더 나아가 생각을 논리 정연한 형태로 결과를 도출하는 방법을 체득할 수 있다. 한 두 편 상황에 맞게 읽는 것 외에도 습관으로 만들어두고 각 연구를 따라가다 보면 지금 현장에서의 업무와는 연관없어 보이더라도 많은 학습점이 존재한다.

만약 마땅하게 읽을 논문을 찾기 어렵다고 생각이 든다면 배우고 싶은 클래스의 실라버스를 웹에서 검색하거나, Coursera나 eDx와 같은 MOOC 수업에서 관심있는 클래스에 참여하면 수많은 참고문헌을 손쉽게 찾을 수 있을 것이다. 대학에 소속되어 있다면 대학에서 가입되어 있는 색인 서비스를 이용해 논문 읽기를 할 수 있고, 소속되어 있지 않더라도 논문 읽기를 시작할 수 있는 좋은 서비스가 웹 곳곳에 산재해 있다. 앞서 언급한 papers we love도 그 중 하나에 해당한다. papers we love에는 CS 논문을 읽고, 공유하고, 배우는 커뮤니티로 리포지터리에 다양한 페이퍼가 포함되어 있다. 이 리포지터리에 등록된 논문 외에도 논문을 찾을 수 있는 여러 페이지 목록을 참고하도록 하자.

학생이나 연구원으로 학교나 교육, 연구기관에 소속되어 있는 상황이 아니라면 논문을 읽는 것은 더 어려운 일이지만 자신의 영역에서 자기 자신에게 도전할 수 있는 좋은 시작점이다. 다음 논문 읽기와 관련해서 번역한 글이 그 시작에 도움이 되었으면 좋겠다. 게

더 읽을 거리

논문 읽기와 관련한 번역 및 포스트를 더 읽고 싶다면 다음 링크를 참고하자.

전공 종사자는 물론 비전공으로 이 분야에 일하게 된 사람이라면 논문을 읽어야 하는가에 대한 고민을 해봤을 것이다. 이 포스트는 Michael Robert BernsteinShould I read papers?를 번역한 글이다. 짧은 글이지만 논문을 읽어야 하는가에 대한 고민을 하고 있다면 조금이나마 자극이 되지 않을까 싶어 번역했다.


제가 논문을 읽어야 하나요?

한 줄 요약: 네.

논문, 논문, 논문. 근래 들어 논문은 아주 많은 주목을 받고 있다. 트위터, 세계 모든 밋업에서, 깃헙 리포지터리에서, 그리고 엄청 많은 사람들이 논문을 읽는 것에 관해 얘기하고 있다. 이 논문에 대한 모든 대화에서 사람들은 이 질문을 빼놓지 않는다.

"제가 논문을 읽어야 하나요?"

내 생각엔 그래야 한다. 난 공식적으로 좀 자주 얘기하는 편이고 다양한 이유로 계속 주장하는데, 논문을 읽는 것은 어떤 분야든 상관없이 모든 사람에게 좋은 생각이기 때문이다.

"제가 논문을 읽어야 하나요?"

이 세 단어에, "해야" 한다는 가장 도전적인 일일 것이다. 그러니까 논문을 "읽어야" 하냐니 무슨 의미인가? 혹시 내가 그렇게 말한 사람인가? 정말 내가 당신의 행동을 독재하길 원하는 것처럼 보이나?

아니다. 난 정말 그런 뜻이 아니다. 논문을 읽고 싶지 않다면 읽지 마라. 안 읽어도 괜찮다. 하지만 당신이 해야 한다고 생각하는 이유는 다음과 같다.

여기서 읽어야라는 표현은 건강한 음식을 먹어야 한다, 가능한 한 깨끗한 공기를 마셔야한다 같은 느낌으로 사용한 것이다. 자기 일에서 동기를 가져야 한다면, 그 분야를 매일 조금씩이나마 이해하고 자신을 향상하기 위해서 무엇이든 해야 한다고 생각한다. 나는 그런 의미에서 사용했다.

"제가 논문을 읽어야 하나요?"

여기에서 "제가"는 누구를 의미하는가? 현재 내 환경에서는 컴퓨터와 어떻게든 연관을 맺고 있는 사람들이지만, 먼저 소프트웨어 개발자를 지칭하고자 한다.

나는 운 좋게도 컴퓨터로 뭔가 만드는 방법을 알려주는 멋진 사람들과 함께 공부할 수 있었고 그 사람들이 어떻게 일하는지 알아야 할 필요가 있었다. 컴퓨터가 제공하는 예술적, 창의적 도구를 현재 상태 그대로 받아들이지 않고서, 속을 살피고, 모방하고, 그리고 자기 자신만의 도구를 만들었다.

만약 자신이 컴퓨터 앞에 매일 앉아 자신 또는 다른 사람들을 위해 무언가를 만드는 사람 중 하나라면, 당신은 "제가"에 해당한다.

"제가 논문을 읽어야 하나요?"

기가 막힌 인류 언어의 탄력성 덕분에 여기서 "읽다"의 문맥은 아주 큰 의미를 지닌다. 읽다가 하나의 의미만 갖는가? 만약 하나의 의미만 있다면 다음처럼 얘기해야 한다:

"만약 처음부터 끝까지 모든 내용을 파고들었다면 그건 무언가를 읽은 것이다."

하지만 "제가 논문을 읽어야 하나요?"에서 "읽다"의 의미는 아주 멋지게 좁은 정의로 사용되었다. 논문을 읽는 것은 논문과 가장 표면적으로 소통하는 방법이다:

  • 인용된 연구를 훑어본다.
  • 저자의 이름을 구글에서 검색하고 무슨 일을 했는지 확인한다.
  • 초록에 있는 내용을 살펴보고 그 분야의 지식을 얻는다.

논문을 "읽는" 것은 논문과 어떻게 관계하는가에 해당한다. 물론 장기간에 걸쳐 읽을 수 있다. 자리 펴고 앉아 "읽기 좋은 글이군" 하며 오랜 시간을 써 읽을 수도 있다. 하지만 빠르게 읽고서 그 글에 읽을 가치가 있다면, 그때 가서 다시 읽고 또 읽으면 된다.

다시 말해서, "너무 멍청하게" 논문을 읽을 필요는 없다. 누구든, 어떤 논문이든 원하는 시간에 읽을 수 있다. 겁내지 말고 용기를 가져라. 무언가를 배우게 될 것이다.

대신 "논문을 읽는 것은 블로그 글이나 소설을 읽는 것과 다르다"는 점을 기억하고 당신은 혼자가 아니라는 점을 잊지 말자.

질문이 "제가 논문을 읽어야 하나요?"지만 정말 혼자인 경우는 드물다. 어딘가에는 분명 당신과 비슷한 길로 가는 사람이 존재한다. 단지 그 사람을 아는 것 뿐만 아니라 거의 서로 만날 수도 있다.

"제가 논문을 읽어야 할까요?"

드디어 이 부분에 도달했다. (이 부분이 맨 처음 나왔어야 했나?) 무엇이 "논문"인가? 논문은 양식에 맞춰 작성해서 구체화한 개념이다. 논문은 작을 수 있다. 하지만 그 크기에 비해 엄청난 내용일 수도 있다.

이 논문은 누군가 찾은 아이디어 일 수도 있고, 인생 내내 연구한 결과거나, 또는 그사이 어떤 것이든 될 수 있다.

앞서 읽는 것에 대한 정의와 같이 유연하게 접근하면 논문에 대해 어떻게 생각해야 하는지 알 수 있다. 논문을 작성했다고 그 작성한 사람 또는 사람들이 당신보다 "똑똑할" 필요는 없다. 그들은 분명 우리와 다른 경험을 갖고 살고 있을 것이다. 그들이 어디서 오고 무엇을 하려고 하는지 누가 알겠는가?

결론

"제가 논문을 읽어야 하나요?"에 대한 내 답은 완전히 yes다. 내 진짜 의미로는, 부담스러울 정도로 읽어야 한다.

자기 자신에게 도전하라.

상상할 수 있는 것보다 훨씬 많은 부분이 표면 아래 숨어있다.

이 글을 작성할 수 있게 도와준 Zeeshan Lakhani, Tom Santero, James Golick, Alex Kahn, Ken Keiter에게 감사의 말을 전한다.

Jennifer Raff블로그는 최근에야 알게 되었는데 자기 연구 분야에 대해 공유하는 포스트가 인상적인 내용이 많았다. 또한, 연구 분야 외에도 비전문가를 위해 논문을 읽고 분석하는 방법을 제시하면서 실제로 그 과정을 보여주는 포스트도 인상 깊었다. 이 포스트는 Jennifer의 How to read and understand a scientific paper: a guide for non-scientists를 번역한 글이다. 글에서 언급했듯 모든 내용을 동의하지 않을지도 모르지만 논문을 읽어본 경험이 없다면 좋은 지침이 되리라 생각한다. 그리고 앞서 번역한 글과는 또 다른 접근 방식을 채택하고 있다. 모든 일이 그렇듯 한가지 방법만 있는 것이 아니므로 자기 상황에 맞는 방법을 전략적으로 참고하면 좋겠다.


지난주 포스트(예방 접종에 대한 진실: 당신의 의사가 구글 대학보다 더 많이 알고 있습니다)가 열띤 토론에 불을 지폈고, 댓글을 작성한 몇명은 이 글로 인해 자신이 작성한 논문에 대한 오류가 입증되기 때문에 나(그리고 다른 독자)를 설득하려 했다. 그 댓글을 읽고 자기 생각을 덧붙이라고 권장하기 전에, 토론을 해볼 만 한 더 큰 이슈에 초점을 맞춰보고 싶다. 무엇이 과학적 권위를 구성하는가?

이 이슈는 단순하게 즐거운 학술적 문제에 그치지 않는다. 과학이 잘못된 방향으로 가는 것은 실제로 매우 중대한 문제다. 예를 들면, "독소"에 대한 두려움과 기도로(또는 식이요법, 운동, 그리고 "청결") 질병의 감염을 예방할 수 있다는 생각으로 커뮤니티가 아이들에게 예방 접종을 하지 않았을 때, 문제는 폭발적으로 나타나게 된다.

"회의적으로 접근하라. 하지만 증거를 찾는다면, 증거를 수용해라." – Michael Specter

충분한 증거를 구성하는 것은 무엇인가? 이 질문에 대해서는 모두 다른 답을 갖고 있다는 것은 명백하다. 하지만 과학적 주제에 대해 제대로 된 교육을 받은 의견이 형태를 갖추기 위해서는, 해당 영역에서 진행되는 현재 연구와 친숙해질 필요가 있다. 그러기 위해서 "주요 연구 논문(primary research article)" (종종 "문헌" 이라 불리는) 을 읽어야 한다. 이전에 과학 논문을 읽어봤다면 난해하고 허풍스런 문체와 익숙지 않은 용어에 좌절한 경험이 있을 것이다. 나도 같은 경험에 대한 기억이 있다. 연구 논문을 읽고 이해하는 것은 모든 박사와 과학자가 학부 시절에 습득하는 기술이다. 어떤 기술이든 인내와 연습으로 얻을 수 있는 것처럼 당신도 이 기술을 배울 수 있다.

나는 사람들이 더 많이 과학적으로 문헌에 기반을 둔 의견을 낼 수 있도록 돕고 싶다. 그래서 비전문가라도 어떻게 과학적 연구 논문을 읽고 이해하는데 어떻게 접근해야 하는지에 대해 안내하기 위해 이 글을 작성했다. 이 글은 과학이나 의학에 배경이 전혀 없는 사람에게 적합하다. 또한 독자가 논문에 대한 기초적인 이해와 그 연구가 제대로 된 연구인지 아닌지에 대한 결정을 내릴 수 있는 것으로 가정하는 것을 기본으로 한다.

여기서 이야기하는데 인용하고자 하는 과학 논문은 주요 연구 논문이다. 이 논문은 특정 질문(또는 질문들)에 대한 새로운 연구로, 상호 비평(peer-reviewed)을 받은 보고서에 해당한다. 또 다른 유용한 형태의 출간문은 **비평 논문(review article)**이다. 비평 논문 또한 교차 평가를 받았고 새로운 정보를 제시하기보다는 여러 주요연구문헌의 결과를 요약해 어떤 합의와 논쟁, 결론 없는 질문이 이 분야에 있는지 요약한다. (여기서 이 내용에 대해 더 많이 작성하진 않지만 비평 논문을 읽을 때는 주의해야 한다. 이런 논문은 출간 당시의 상황을 기준으로 하기 때문이다. 2003년에 출간되어 2001년에 연구된 연구를 비평한 내용을 2013년에 읽는다면 아주 유용하지는 않을 것이다. 대부분의 연구는 비평 논문이 발표된 그해에 이미 그 연구 내용을 대부분 변경한다.)

시작하기 전에: 몇 가지 일반적인 조언

과학적 논문을 읽는 것은 과학에 대한 블로그나 신문기사를 읽는 것과는 완전히 다른 과정이다. 단순히 다른 순서로 정리된 섹션을 읽는 것뿐만 아니라 읽으며 필기하고, 여러 번 반복해서 읽고, 더 자세한 내용을 보기 위해 다른 논문도 아마 살펴봐야 할 것이다. 처음에는 논문 하나 읽는데도 시간이 오래 걸린다. 인내심을 가져라. 경험이 늘어날 때마다 이 과정은 더욱 빨라진다.

대다수의 주요 연구 논문은 초록(Abstract), 서론(Introduction), 절차, 결과, 그리고 결론/해석/토론으로 구성된다. 이 순서는 어느 저널에서 출간하느냐에 따라 달라진다. 어떤 저널에서는 연구에 대한 중요 내용이 포함된 추가 파일을 요구한다. (온라인 정보 부록 Supplementary Online Information 이라 부른다.) 만약 이런 저널에 온라인으로 출간할 때는 논문에 포함되어야 한다. (이 파일을 건너뛰지 않도록 주의해야 한다.)

논문 읽기를 시작하기 전에 저자와 협회를 기록한다. 협회에 따라 제출자를 잘 존중하기도 하지만 (e.g. University of Texas) 어떤 협회는 법적으로는 연구 협회지만 실제로는 의제 중심인 경우도 있다. (e.g. Discovery Institute) 팁: "Discovery Institute"를 구글에서 검색해보면 여기에서 나온 논문이 진화론과 관련해 과학적 권위를 갖기 어렵다는 사실을 알 수 있다.

또한, 저널이 어디에서 출간되었는지도 기록한다. 좋은 평판의 (생물의학) 저널은 Pubmed와 같은 곳에 색인이 있다. 물론 생물의학 저널이 아니라면 Pubmed에 없다. 더 넓은 과학 영역에서의 저널 색인은 Web of Science에서 확인할 수 있다. 불확실한 저널은 주의한다.

읽는 동안, 이해할 수 없는 __모든 단어__를 적고 확인해봐야 한다. (그렇다, 모든 단어다. 이게 완전 고통스럽다는 것은 잘 알고 있다. 하지만 어휘를 이해하지 못하면 이 논문을 이해할 수 없다. 과학에서 쓰이는 어휘는 극단적으로 정확한 의미를 담고 있다.)

주요 연구 논문을 읽는 단계별 방법

초록이 아닌 서론부터 읽기 시작한다

초록은 밀도 높은 첫 단락으로 논문에서 가장 앞에 위치한다. 많은 비과학자가 논문에서 이 부분을 먼저 읽고 과학적인 논의를 시작한다고 하는 경우가 종종 있다. (최악의 관례다. 절대 하지 말아야 한다.) 나는 읽기 위한 논문을 고를 때, 제목과 초록의 조합에 기초해 내 흥미와 연관성이 있어 보이는 논문을 고른다. 하지만 깊게 읽기 위해 조합된 여러 논문 묶음이라면, 나는 항상 초록을 가장 __마지막__에 읽는다. 이런 방식을 사용하는 이유는 초록이 전체 논문의 내용을 간결하게 요약한 것으로 이것을 먼저 읽는 것으로 연구 결과에 대한 저자의 해석을 무의식중에 택하게 되는 경우가 발생할지도 모른다는 우려 때문이다.

큰 질문을 확인한다

"이 논문은 무엇에 대한 것인가" 식의 질문이 아니라 "이 전체 영역에서 해결하려고 하는 문제는 무엇인가"에 대해 질문해야 한다.

이 질문은 왜 이 연구가 수행되는가 하는 점에 초점을 맞출 수 있다. 주제에 대한 연구에 어떤 동기부여가 있었는지 그 증거를 자세히 확인한다.

다섯 문장 또는 그보다 적게 논문의 배경에 대해 요약한다

여기 도움이 될 만한 질문이 있다:

이 분야에서 큰 질문에 답변하기 이전까지 완료된 연구는 무엇인가? 그 연구에서의 제약은 무엇인가? 저자에 따르면 다음 완료되어야 할 연구는 무엇인가?

이 다섯 가지 질문에 대한 것은 좀 제멋대로인 경향이 있지만, 연구의 맥락에 대해 생각하게 하고 간결하게 접근할 수 있도록 강제성을 준다. 연구를 이해하기 위해 왜 이 연구가 완료되었는지에 대해 설명할 수 있어야 한다.

세부적인 질문을 확인한다

저자가 이 연구를 통해 정확히 답변하고자 하는 것은 무엇인가? 이 질문엔 여러 답이 있을 수도 있고 단 하나의 답이 있을 수도 있다. 그 질문을 적어본다. 만약 그 연구를 한번 또는 그 이상 점검해서 비어있는 가설(귀무가설)이 있는지 확인한다.

_비어있는 가설_이 어떤 의미인지 확신이 없다면 이 글을 읽어본다. 내가 앞서 작성한 포스트에서 언급한 글(이런 글)을 읽고 어떤 부분이 비어있는 가설인지 확인한다. 물론 모든 논문에 대해서 비어있는 가설을 확인할 필요는 없다는 사실을 기억하자.

접근 방식을 확인한다

저자가 세부적인 질문들에 대해 어떻게 답변하려고 하는가?

이제 절차(methods)를 읽는다. 각각의 실험에 대한 다이어그램을 그리고 저자가 정확히 무엇을 했는지 표현한다.

내 뜻은 말 그대로 그림을 그려야 한다. 그 연구에 대해 완전히 이해할 수 있을 만큼 세부적인 내용을 포함한다. 다음 그림은 내가 오늘 읽은 논문의 절차를 그림으로 그린 것이다. (Battaglia et al. 2013: “The first peopling of South America: New evidence from Y-chromosome haplogroup Q”) 이 그림은 물론 당신이 필요로 하는 것보다 훨씬 적은 내용을 담고 있는데 이 논문은 내 분야에 해당하고 나도 이런 절차를 항상 사용하기 때문이다. 하지만 만약 이 내용을 읽다가 "네트워크를 통해 감소-미디안 절차로 데이터를 처리한다"가 무슨 의미인지 모른다면 찾아봐야 한다.

이 연구의 세부적인 내용에서 반복적으로 사용되는 절차에 대해 이해할 필요는 없다. 그 일은 논문 리뷰어가 해야 하는 일이다. 하지만 기초적인 절차에 대해 다른 사람에게 설명할 정도가 되지 않는다면 결과를 읽을 준비가 되지 않은 것이다.

결과를 읽는다. 각각의 연구, 수치 그리고 표에서 나타나는 결과를 하나 또는 그 이상의 문단으로 요약한다. 아직 그 결과가 어떤 의미가 있는지 정하지 않아도 된다. 그 결과가 무엇인지에 대해서만 작성한다.

일반적으로 좋은 논문이라면 가장 결과의 중요한 부분은 수치와 표에 요약되어 있다는 점을 찾을 수 있을 것이다. 이 부분을 유의해서 보자. 몇 결과를 확인하기 위해서 온라인 정보 부록을 확인해야 할 수도 있다.

만약 이 논문에서 사용된 통계적 질의에 대해 이해할 수 있는 충분한 배경지식이 없다면 이 부분이 어려워질 수 있다. 이 포스트에서 통계에 대해 가르칠 수 없지만 다음 글(1, 2, 3)에서 기초적인 부분을 배울 수 있을 것이다. 이 내용에 대해 친숙해질 것을 강력하게 권한다.

결과에서 주의깊게 봐야 할 부분

  • "유효한 significant"나 "유효하지 않은 non-significant" 같은 표현은 언제든 사용할 수 있다. 이 단어는 명확한 통계적 의미를 담고 있다. 이 내용은 이 글을 참고한다.

  • 그래프가 있다면 거기에 오차 막대가 포함되어 있는가? 특정 분야 연구에서는 자신감 결여로 큰 차이가 발생하는데 주요한 적신호로 볼 수 있다.

  • 표본 규모. 이 연구의 대상이 10명인가, 10,000명인가? (연구 목적에 따라 10명 규모의 표본도 충분할 수 있지만, 대다수의 연구는 많을수록 좋다.)

결과를 활용해 세부적인 질문에 답할 수 있는가? 결과의 의미가 무엇이라고 생각하는가?

이 부분에 대해 생각하기 전까지 다음으로 넘어가지 말아야 한다. 저자의 해석에 따라 자기 생각을 바꿀 생각이라면 괜찮다. 이런 분석에 관해 초보자라면 이 고민 없이 넘어갈 것이다. 하지만 다른 사람의 견해를 읽기 전에 자기 자신의 해석을 형성하는 것은 정말 좋은 습관이다.

결론/토론/해석 섹션을 읽는다.

결론에 대해 저자는 어떻게 __생각__하는가? 저자의 주장에 동의하는가? 저자의 해석에 대해 어떤 __대안__이 있는가? 저자의 그 연구에서 확인할 수 있는 약점이 없는가? 저자가 놓친 부분은 없는가? (절대 틀릴 일 없다고 가정하지 않는다!) 다음 단계에 대해 어떤 제안을 하는가? 그 제안에 대해 동의하는가?

이제 처음으로 돌아가서 초록을 읽는다.

저자가 논문으로 얘기하는 부분과 초록이 일치하는가? 논문을 읽고 생각한 해석과 초록이 일치하는가?

마지막 단계: (이 단계를 등한시하지 말 것) 다른 연구자가 이 논문에 대해 어떻게 이야기 하는가?

(자칭이든 타칭이든) 이 분야에서 전문가는 누구인가? 그 전문가가 이 연구에 대해서 비평할 때 이런 식으로 생각해본 적이 없다고 하는가 아니면 전반적으로 지지하는가?

개인적으로 추천하는 방법은 구글을 활용하는 것이다. 하지만 이 과정은 마지막에 해야 한다. 그래서 다른 사람들이 어떻게 생각할지 비판적으로 생각하고 미리 준비할 수 있게 된다.

그리고 이 단계는 어떤 분야의 논문을 읽는가에 따라 다른 부분이다. 나에게는 아주 중요하다! 인용된 문헌을 확인해서 저자가 어떤 논문을 인용했는지 확인한다. 이 과정은 이 분야에서 중요한 논문을 알 수 있게 된다. 그리고 내 글이 인용되었는지 확인할 수 있... 이건 농담이다. 이 과정으로 유용한 아이디어, 기술의 원천을 찾는 데 도움이 된다.

이제 더 큰 일에 도전할 수 있게 되었다. 다음 주에는 쟁점이 많은 논문을 읽을 때 위 방법을 활용해보자. 어떤 식으로 읽고 싶은가? 지난주에 게시한 논문을 함께 읽고 비평해보는 것도 좋을 것 같다. 만약 예제를 보고 싶으면 예제: 어떻게 예방접종 안전에 대한 연구를 읽는가를 확인한다.


감사하게도 논문을 어떻게 비평적으로 읽고 분석하는가에 대한 방법을 José Bonner, Bill Saxton 교수님께 배울 수 있었다. 이 방법을 배울 기회가 있던 것은 영광이었다.

추가하고 싶은 조언, 전혀 다르지만, 더 낫다고 생각하는 방법이 있거나, 추가적인 질문, 다른 유용한 자료가 있다면 댓글을 남겨주기 바란다.

이 글은 Peter G. Klein가 작성한 How to Read an Academic Article을 번역한 포스트로, 학술 논문을 어떻게 읽어야 하는가에 대한 전략을 제시하고 있다. 내용에서 언급되는 것처럼 당연한 이야기를 적은 목록이라고 느껴질 수 있지만, 학술 논문을 읽는 것 이외에도 정보를 수용하고 생산하는 데 있어 좋은 관점을 갖고 있어 번역하게 되었다.


이번 가을에 학부 1학년 학생을 대상으로 "기관 및 조직의 경제학"을 가르치고 있다. 이 강의에서 읽어야 하는 목록은 대다수의 학부 코스, 그리고 1학년 대상의 미시경제학이나, 계량경제학 등에 비해서 좀 많은 편이다. 학생들에게 단순하게 열정적인 독자뿐 아니라 능률적인 독자가 돼야 할 필요가 있다고 설명한다. 그래야 학술적 기사에서 최소한의 노력을 통해 최대한의 정보를 얻을 수 있기 때문이다. 다시 말해 훑어보기(skim)의 예술을 배울 필요가 있다.

지금까지 학생들에게 훑어보는 것에 관해 설명했을 때, 대부분 어떻게 훑어봐야 하는지 모르겠다고 반응했다. 그래서 몇 년 전에 그 방법에 대해 작은 안내지 "어떻게 학술 논문을 읽어야 하는가"를 작성해 몇가지 팁과 방법을 적었다. 이 안내지는 거만하게 내 방식에 대해 알려주고자 하는 의미가 아니란 점을 강조하고 싶다. 그리고 이 안내지의 내용이 너무 당연한 이야기라고 느껴진다면 무시해도 문제 없다. 대다수 학생은 이 안내지로 도움을 받았다며 고마움을 표현했다. 그래서 이 안내지를 아래 첨부했다. 내용을 향상하기 위한 댓글이나 추천은 언제나 환영한다.

어떻게 학술 논문을 읽어야 하는가

  1. 경고: 모든 경우에 통하는 한가지 방법은 없다!
  2. Klein의 기본적인 훑어보기, 탐색하기, 처리하기...
    1. 초록(abstract)을 읽는다. (만약 있다면)
    2. 서론(introduction)을 읽는다.
    3. 결론(conclusion)을 읽는다.
    4. 중간 내용을 훑어본다. 섹션의 주제, 표, 수치 등 - 논문의 스타일, 흐름을 따라가 느낄 수 있도록.
      1. 이 논문은 방법론적인가, 개념적인가, 이론적인가(구술에 기반을 뒀나, 수학적인 방법에 기반을 뒀나), 실증적인가, 또는 그 외의 접근 방법인가.
      2. 이 논문은 설문조사에 기초하는가, 새 이론적 기여에 기초하는가, 기존에 존재하던 이론이나 기술에 대한 실증적 타당성을 확인했나, 비평인가, 또는 그 외의 방법인가.
    5. 다시 처음으로 돌아가 등식, 대부분 표와 수치를 건너뛰고 모든 내용을 빠르게 읽는다.
    6. 다시 처음으로 돌아가 전체 내용을 주의 깊게 읽는다. 가장 중요하게 보이는 섹션 또는 영역에 집중해 읽는다.
  3. 이 논문에서 저자가 하려는 일에 관해 주장의 근거를 이해했다면 그 내용을 비평하라.
    1. 만약 논거가 타당하다면 물어봐라. 그 내용이 내부적으로 일치하는가? 주장과 그에 대한 근거가 타당한가? (이 방법은 경험을 키우는 데 도움이 된다.)
    2. 이전에 읽은 같은 주제 또는 비슷한 주제의 논문과 비교하라. (만약 이 논문이 이 영역에서 첫 번째로 읽은 논문이라면, 비슷한 논문을 더 찾아 훑어 본다. 서론과 결론이 열쇠다.) 비교하고 대조해본다. 논거에 일관성이 있는가, 모순되는 내용이 있는가, 직교하는(orthogonal) 부분이 있는가?
    3. Google 학술 검색, Social Sciences Citation Index, 각 출판사 웹 페이지, 학술기사를 찾을 수 있는 곳에서 읽은 학술 논문에서 인용된 자료를 찾는다. 그 자료에서는 어떻게 언급되었는지 살펴본다. 블로그나 그룹 등에서 언급된 내용도 살펴본다.
    4. 인용된 자료를 확인한다. Journal of Economic Literature에서 진행한 설문 자료, 핸드북, 백과사전 또는 이와 유사한 출처의 자료 등을 확인하고 논문에서의 주제와 얼마만큼 해당 영역에서 접점을 가졌는지 검증한다.

FP in Elm의 week 1-2 Intro to FRP in Elm 정리 포스트다.


Introduction to FRP in Elm

JS 이벤트 리스너 코드 예제를 보여주면서 IsDown같은 변수를 만들어 상태를 저장해야 하는 부분, 콜백으로 지정하는 복잡성 등에 얘기하며 FRP에서는 더 쉽게(?) 할 수 있다고 이야기.

Signal이란

“시그널은 시간에 따라 변동되는 값” 원시적 시그널은 Signal Bool을 반환.

함수형 프로그래밍 블럭을 만들어서 시그널을 추상화하거나 합성할 수 있음.

Signal.map : (a -> b) -> Signal a -> Signal b
isUp = Signal.map (fun curValIsDown -> not curValIsDown) Mouse.isDown
-- or,
isUp = Signal.map not Mouse.isDown

이제 Mouse.isDown 시그널이 업데이트 되면 자동으로 반영됨.

Signal.map과 같이 함수를 각각에 맵핑하는 것은 다른 데이터에서도 많이 사용. List.map, String.map, Maybe.map, Dict.map, etc.

HTML로 렌더링하기

main에 정의하는 것으로 HTML을 렌더링 할 수 있음. Graphics.Element

elm-repl을 사용할 수 없으므로 elm-make, 온라인 에디터를 활용, 또는 로컬 환경을 활용.

Text.plainText는 최근 버전에서 삭제되서 구버전을 설치하거나 changelog를 참고해야 한다. 아래는 [Text](http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Text) 를 참고했다.

import Text exposing (fromString)
import Graphics.Element (Element, leftAligned)

plainText = leftAligned << fromString

main : Element
main = plainText "Hello World!"

Text.fromStringStringText로 변환하고 Graphics.Element.leftAlignedTextElement로 변환해준다. 코드에서 필요한 함수를 import 한 것도 확인할 수 있다.

실제로 mainSignalElement로 정의되어 있다. 위와 같이 다른 Element는 Signal.constant를 이용해 Signal로 변환된다.

Signal.constant : a -> Signal a

위 예제를 더 명시적으로 작성하면 다음과 같다.

main : Signal Element
main = Signal.constant(plainText "Hello World!")

위 내용으로 작성한 예제.

import Text exposing (fromString)
import Graphics.Element exposing (Element, leftAligned)
import Mouse
import Signal

plainText = leftAligned << fromString
isUp = Signal.map not Mouse.isDown

main : Signal Element
main = Signal.map (\b -> plainText(toString b)) isUp

import에 대해

매번 쓸 때마다 앞 코드처럼 map하지 말고 다른 모듈로 만들어서 재활용하라는 이야기.

함수 합성에 대해

다음은 함수를 합성하는 여러 방법. 취향에 맞게 선택을 하라는데 가장 마지막 방식이 많이 쓰는 모양.

(\b -> b |> toString |> plainText)
(toString >> plainText)
(\b -> plainText <| toString <| b)
(plainText << toString)

Folding From the Past

클릭 횟수를 보여주는 페이지를 만들려고 함. 이런 signal은 내장되어 있지 않은데 다음과 같이 모조 유닛 ()으로 된 이벤트를 정의할 수 있음.

Mouse.clicks : Signal ()

기본 MVC 아키텍쳐

-- Model
type alias State = Int

initState : State
initState = 0

alias는 새 타입 정의 없이 Int와 동일한 역할을 하는 State를 만들어줌. abbr 만들 때도 쓸 수 있고.

-- View
view : State -> Element
view s = asText s
-- 더 간결하게
view = asText

asTextTextasText에서 Graphics.Elementshow변경됨.

컨트롤러는 시그널이 갱신되었을 때, 상태를 변형하거나 렌더링하는 함수를 시그널과 연결해주는 역할을 한다. 타입 a는 전체 값, 타입 b는 초기값, 마지막 타입 b는 최종 상태값이 된다.

Signal.foldp : (a -> b -> b) -> b -> Signal a -> Signal b

List.foldl : (a -> b -> b) -> b -> List a -> b
List.foldr : (a -> b -> b) -> b -> List a -> b

folding from the past 는 from the left라는 말이고 from the future는 from the right이 된다. 시그널은 folding from the past로 호출한다.

step : a -> State -> State
step _ i = i + 1

main : Signal Element
main =
  Signal.map view (Signal.foldp step initState Mouse.clicks)

다른 예제는 여기.

main = Signal.map VIEW (Signal.foldp UPDATE INIT_STATE BASE_SIGNAL)
-- so,
main = Signal.map view (Signal.foldp step initState (Time.every Time.second))

잠깐만

맨 처음 예로 든 JS와 달리 부수적인 부분은 다 컴파일러가 알아서 함.

JavaScript로의 컴파일링

원초적인 이벤트는 앞서 구현한 JS에서와 같이 다뤄짐. 하지만 elm이 Signal Graph로 처리해 순수 함수 형태로 정의된 Signal을 사용할 수 있게 함.

각각의 기능이 순수한 함수 형태로 다른 함수에 영향을 주지 않아 전체를 컴파일 할 필요가 없어짐. 이런 접근 방식은 함수형 언어 컴파일러 최적화와 관련되어 중요한 개념 중 하나.

2D 그래픽

Graphics.Element, Graphics.Collage 공부할 것.

읽을 거리

필수

  • 라이브러리 Signal, Graphics.Element, Graphics.Collage

Signal에 정의된 (<~)는 위에서 정의한 isUp을 간편하게 정의하는데 유용함.

isUp = not <~ Mouse.isDown
-- `Mouse.isDown` 시그널을 `not` 함수를 통해 전달

추천

심화

FP in Elm의 week 1-1-2 Intro to ML in Elm 정리 포스트다.


Introduction to ML in Elm

Elm은 웹사이트에서 받아 설치한다. REPL로 진행한다.

$ elm-repl
Elm REPL 0.4.2 (Elm Platform 0.15.1)
  See usage examples at <https://github.com/elm-lang/elm-repl>
  Type :help for help, :exit to exit
> True
True : Bool
> False
False : Bool
> 'a'
'a' : Char
> "abc"
"abc" : String
> 3.0
3 : Float

numberIntFloat 모두를 의미. 타입 검사기가 알아서 선택.

> 3
3 : number
> truncate 3
3 : Int
> truncate 3.0
3 : Int

number가 하스켈에서 type 클래스로 미리 정의한 것처럼 보이지만 Elm엔 일반적으로 타입 클래스 지원이 없음. 이 number는 몇가지 특별한 용도로 사용할 수 있는 클래스 중 하나.

Tuples

두번째 튜플에서 ‘가 여러개 붙는 이유는 모르겠다. 타입이 달라질 수 있어서 그런 것 같기도. 튜플에 컴포넌트가 하나면 튜플이 아닌 것으로 취급.

> (True, False)
(True,False) : ( Bool, Bool )
> (1,2,3,4.0)
(1,2,3,4) : ( number, number', number'', Float )

> ("Leave me alone!")
"Leave me alone!" : String
> (("Leave me alone!"))
"Leave me alone!" : String

Functions

인자 하나, 반환값 하나를 갖는 함수:

> exclaim = \s -> s ++ "!"
<function> : String -> String
> exclaim s = s ++ "!"
<function> : String -> String
> exclaim "HI"
"HI!" : String

uncurried/curried 스타일 다인자 함수:

> plus (x, y) = x + y
<function> : ( number, number ) -> number
> plus = \(x, y) -> x + y
<function> : ( number, number ) -> number
> plus xy = fst xy + snd xy
<function> : ( number, number ) -> number

> plus x y = x + y
<function> : number -> number -> number
> plus x = \y -> x + y
<function> : number -> number -> number
> plus = \x -> \y -> x + y
<function> : number -> number -> number

curried 함수를 활용한 부분 애플리케이션:

> plus3 = plus 3
<function> : number -> number
> plus3 5
8 : number
> plus3 3.0
6 : Float

number 타입 캐스팅을 어떻게 할 것인가. number -> Int는 만들 수 없지만 어짜피 number는 Int가 필요할 때 자동으로 변하니 그냥 작성.

> toInt n = n // 1
<function> : Int -> Int
> plusInt x y = (toInt x) + y
<function> : Int -> Int -> Int
> plusInt x y = (toInt x  + y)
<function> : Int -> Int -> Int

타입 어노테이션 Type Annotations

대부분의 ML 방언과 같이 자동으로 처리하지만 최상위 레벨에서 수동으로 지정해야 좋은 경우도 있음. 예제 IntroML.elm 참조.

plus : number -> number -> number
plus x y = x + y

plusInt : Int -> Int -> Int
plusInt x y = x + y

plusInt : Int -> Int -> Int
plusInt = plus

앞에서 toInt 쓴 것과 달리 plusInt에서 명시적으로 어노테이션을 지정하고 위처럼 쓸 수 있음. 클라이언트에게 공개되는 API보다 더 범용적인 코드를 구현하는 것을 강조.

모듈 불러오기 Importing Modules

앞서 IntroML.elm을 받는다. exposing을 사용하게 변경되었다. (..)은 모듈 내 모든 함수를 노출하게 된다. 모듈을 약어로 부를 때는 as 키워드를 사용한다.

> import IntroML
> IntroML.plusInt 2 3
5 : Int
> import IntroML exposing (plusInt)
> plusInt 2 3
5 : Int
> import IntroML exposing (..)
> exclaim "Awesome"
"Awesome!" : String
> import IntroML as M
> m.plusInt 2 3
5 : Int

Basics, Maybe 등이 포함된 일반 라이브러리는 기본으로 불러오게 됨.

조건문

> if 1 == 1 then "Yes" else "No"
"Yes" : String
> if False then 1.0 else 1
1 : Float
> if | 1 == 1 -> 1.0 \
|    | 1 == 2 -> 1
1 : Float
> if | 1 == 1     -> 1.0 \
|    | otherwise  -> 1
1 : Float

otherwise는 True : Bool. 다중조건문 multi-way-if는 조건 중 참으로 평가되는 경우가 없으면 런타임 에러가 발생. 실행되지 않을 조건이 있다면? 닿지 않는 코드는 평가도 하지 않음. 다중조건문에서 줄 맞추는 것 잊지 말 것.

다형성 타입 Polymorphic Types

타입변수는 소문자로 시작하거나 한글자로 지정되는 경우가 많음.

> choose b x y = if b then x else y
<function> : Bool -> a -> a -> a
> choose True True False
True : Bool
> choose True "a" "b"
"a" : String
> choose True 1.0 2.0
1 : Float
> choose True 1 2
1 : number
> choose True 1 2.0
1 : Float

만약 다음과 같이 타입 어노테이션을 지정한다면 다형성 타입이지만 타입을 강제할 수 있음.

choose : Bool -> number -> number -> number

Basics에서 작성된 비교 연산자에는 comparable 이라는 특수 목적의 타입 변수가 존재함.

> (<)
<function> : comparable -> comparable -> Bool
> 1 < 2
True : Bool
> 1 < 2.0
True : Bool
> "a" < "ab"
True : Bool
> (2, 1) < (1, 2)
False : Bool
> (1 // 1) < 2.0 -- 타입 불일치
> True < False -- 타입 불일치

딴짓: 버그잡기

버그가 이미 잡혀서 내용 결과가 다름. 딴짓 실패.

버그를 찾으면 메일링 리스트에서 검색하고 버그 리포트를 남기자는 이야기.

리스트

cons 연산자는 코스에선 제안된 기능인데 이미 반영되서 import 할 필요 없음. 제안 기능은 불러와서 쓸 수 있는 모양. (::)는 OCaml 문법 , 는 하스켈 문법이라고.

> 1::2::3::4::[]
[1,2,3,4] : List number
> [1,2,3,4]
[1,2,3,4] : List number
> [1..6]
[1,2,3,4,5,6] : List number
> [1.0..6.0]
[1,2,3,4,5,6] : List Float

하스켈은 String이 List Char인데 여긴 아니라고.

> ['a','b','c']
['a','b','c'] : List Char
> "abc"
"abc" : String
> ['a','b','c'] == "abc" -- 타입 불일치

case 한줄로 쓰기. (갑자기 난이도 점프를 시도한 느낌.)

> len xs = case xs of {_::xs -> 1 + len xs; [] -> 0}
> len [1..10]
10 : number
> len ['a', 'b', 'c']
3 : number

글자 수를 세는 len이라는 함수를 정의했다. 리스트를 xs로 받아서 리스트 가장 앞에 녀석을 하나 빼 글자수 1을 더하고 나머지 xs를 다시 len 함수에 보내 계속 글자 수를 알아낸다. 계속 반복해서 빈 리스트 []가 되면 0을 반환해 전체 글자 수를 얻게 된다!

case가 모든 결과를 처리하지 못하면 완전하지 않은 패턴이 발견되었다고 런타임 에러가 발생한다. 다중 case문에서는 두번째 줄부터 맨 앞에 공백을 넣어야 한다.

> hd xs = case xs of \
|   x::_ -> x
<function> : List a -> a
> hd [2]
2 : number
> hd []
Error: Runtime error in module Repl (between lines 4 and 5)
Non-exhaustive pattern match in case-expression.
Make sure your patterns cover every case!

고차함수

> import List exposing (filter, map, foldr, foldl)
> filter
<function> : (a -> Bool) -> List a -> List a
> filter (\x -> x `rem` 2 == 0) [1..10]
[2,4,6,8,10] : List Int
> map
<function> : (a -> b) -> List a -> List b
> map(\x -> x ^ 2) [1..10]
[1,4,9,16,25,36,49,64,81,100] : List number
> foldr
<function: foldr> : (a -> b -> b) -> b -> List a -> b
> foldr (\x acc -> x :: acc) [] [1..10]
[1,2,3,4,5,6,7,8,9,10] : List number
> foldl
<function: foldl> : (a -> b -> b) -> b -> List a -> b
> foldl (\x acc -> x :: acc) [] [1..10]
[10,9,8,7,6,5,4,3,2,1] : List number

(::)도 함수라서 다음처럼 가능.

> (::)
<function> : a -> List a -> List a
> foldl (\x acc -> (::) x acc) [] [1..10] -- eta-expanded version
[10,9,8,7,6,5,4,3,2,1] : List number
> foldl (::) [] [1..10] -- eta-reduced version
[10,9,8,7,6,5,4,3,2,1] : List number

데이터타입

리스트는 귀납형 데이터 타입이라 직접 데이터 타입을 정의 할 수 있음. (enum 같은 느낌.) 값을 가지지 않을 수도, 가질 수도 있음.

> type Diet = Herb | Carn | Omni | Other String
> Carn
Carn : Repl.Diet
> Omni
Omni : Repl.Diet
> Other "Lactose"
Other "Lactose" : Repl.Diet
> Other -- Non-nullary data constructor는 그 자체로 함수
<function> : String -> Repl.Diet
> diets = [Herb, Omni, Omni, Other "Lactose"]
[Herb,Omni,Omni,Other "Lactose"] : List Repl.Diet

패턴매칭에 유용. 결과가 나오지 않는 case를 작성하지 않도록 주의.

> isHerb d = case d of \
|   Herb -> True \
|   _    -> False
<function> : Repl.Diet -> Bool
> List.map isHerb diets
[True,False,False,False] : List Bool

에러를 위한 타입

앞에서 작성한 hd 함수는 빈 리스트를 넣었을 때 런타임 에러가 발생하고 실패함. 에러를 위한 타입을 만들어서 의미있는 결과를 받도록 처리.

> type MaybeInt = YesInt Int | NoInt
> hd xs = case xs of \
|   x::xs' -> YesInt x \
|   []     -> NoInt
<function> : List Int -> Repl.MaybeInt
> hd [1..4]
YesInt 1 : Repl.MaybeInt
> hd []
NoInt : Repl.MaybeInt

다형성 타입으로 바꾸면,

> type MaybeData a = YesData a | NoData
> hd xs = case xs of \
|   x::_ -> YesData x\
|   []   -> NoData
<function> : List a -> Repl.MaybeData a
> hd [1]
YesData 1 : Repl.MaybeData number
> hd ['a','b','c']
YesData 'a' : Repl.MaybeData Char
> hd []
NoData : Repl.MaybeData a

이 방식은 MaybeData 패턴으로 엄청 일반적이고 Maybe라는 라이브러리도 포함되어 있음.

> type Maybe a = Just a | Nothing
> hd xs = case xs of \
|   x::_ -> Just x \
|   []   -> Nothing
<function> : List a -> Repl.Maybe a

Maybe 패턴을 활용한 Result 가 제공되고 있음. 다음은 예시에서의 코드.

errHead : List a -> Result String a
errHead xs = case xs of
  x::_ -> Ok x
  []   -> Err "errHead: expects non-empty list"

저장해서 불러오면 다음과 같은 결과.

> import MaybeStudy exposing (errHead)
> errHead ["What", "WHhooo"]
Ok "What" : Result.Result String String
> errHead []
Err ("errHead: expects non-empty list") : Result.Result String a

이항 연산자 infix Operators

(<|), (|>), (<<), (>>)를 Basics 문서에서 찾아보라고.

(<|)는 괄호를 입력해야 하는 번거로움을 줄여준다.

leftAligned (monospace (fromString "code"))
leftAligned << monospace <| fromString "code"

(|>)도 괄호를 줄여주고 역순으로 입력하기 때문에 이해하기 더 쉬운 코드가 된다.

scale 2 (move (10,10) (filled blue (ngon 5 30)))
ngon 5 30
  |> filled blue
  |> move (10,10)
  |> scale 2

위 둘은 괄호를 회피하는 것 이외에도 앞서 함수의 치역과 뒤따라오는 함수의 정의역을 일치시키기 위해서 결괏값을 먼저 처리하는 것 같다.

(<<)와 (>>)는 함수 합성에 사용한다.

(g << f) == (\x -> g (f x))
(g >> f) == (\x -> f (g x))

Let 표현식

지역 스코프 한정 변수를 let으로 지정해 선언한다. 앞 공백이 중요함. 그 밑은 같은 결과, 쉬운 표현.

plus3 a =
  let b = a + 1 in
  let c = b + 1 in
  let d = c + 1 in
    d

plus3 a =
  let b = a + 1
      c = b + 1
      d = c + 1
  in
    d

plus3 a = a |> plus 1 |> plus 1 |> plus 1

plus3 = plus 1 << plus 1 << plus 1

읽을 거리

필수

추천

그외

seoh님의 Elm Resources 글에서 [Functional Programming: Purely Functional Data Structures in Elm

]3 강의를 알게 되었다. 개요를 읽고 흥미가 생겨 강의 노트를 읽기 시작했고 나중에 쉽게 찾아보려고 짤막하게라도 정리하기로 했다. 강의에서 사용된 elm이 이미 구버전이라서 최신 버전과 다른 부분이 있어 그 부분은 별도로 적었다. 조금 지나면 내 노트도 금방 구버전이 될 것 같은 기분이지만.

강의 노트 정리 페이지 목록보기


Course Overview

이 코스는 다음 두가지 테마로 진행된다.

  • 효과적인 데이터 구조를 구현하고 분석, 특히 함수형 프로그래밍 언어의 맥락에서
  • 인터렉티브 프로그래밍을 위한 함수형 리액티브 프로그래밍(FRP) 배우기

여기서 사용한 언어는 ML의 방언인 Elm. ML은 1970년대 개발되어 많은 영향을 주고 있다.

  • 평가 전략 Evaluation Strategy: (이른 평가 문법 위에서 동작하는) 지연 평가
  • 타입 Types: 정적 타입, 자동 타입 인터페이스 등 쿨한 기능이 있어 하스켈과는 조금 다르다고.
  • 부작용 Side Effects: 함수형 프로그래밍에서는 가변변수 등 절차형에서 제공하는 기능을 사용할 수 있지만 작게, 지역적으로 사용하길 권장. ML은 타입 시스템 바깥에서 처리(?)하고 하스켈은 타입으로 기록하는 방식으로 차이가 있음.

Elm은 Standard ML, OCaml, F#에 비해 적은 기능만 추가돼 작은 방언이라 얘기한다.

이 코스도 처음으로 진행되고 Elm도 작은 언어에 작은 커뮤니티라 삽질을 각오하고 코스에 임할 것.

다른 프로그래밍 언어와 같은 부분이 많아 큰 어려움은 없었지만 타입 변환 등 자바스크립트만의 특성으로 잘 읽어봐야 할 부분이 많았다. 특히 typeofinstanceof 부분은 JavaScript를 더 이해하는데 도움이 되었다.


Chapter 4 표현식과 연산자 Expressions and Operators

표현식 : 값을 구하기 위한 표현 방법

연산자 : 표현식의 구성요소 (값을 반환하거나 연산함)

4.1 Primary Expressions

상수, 문자열 값, 키워드, 변수 레퍼런스 (eg. true, false, undefined)

4.2 객체, 배열 생성자 Object and Array Initializers

  • 객체 : {}
  • 배열 : []

4.3 함수 정의 표현식

var square = function(x) { return x * x; };

4.4 프로퍼티 접근 표현식

expression.identifier  // 식별자가 이렇게 써도 문법에 위배되지 않을 때 사용 가능
expression[expression] // 배열일 때 특히

4.5 구동 표현식 Invocation Expressions

square(10)
Math.min(x, y, z)
foos.sort()
  • 아규먼트의 연산을 먼저 한 후 함수가 실행됨 (in common sense)
  • 실행 가능 객체가 아니라면 TypeError
  • return
  • 함수(OO에선 메서드)가 실행될 때 this

4.6 객체 생성 표현식

var o = new Object()
var d = new Date()
  • 초기화 할 때 생성되는 this

4.7 연산자 미리보기

  • p.62 표 참조. 다른 프로그래밍 언어랑 크게 다른게 없음.
  • typeof, instanceof
  • 문자열 합칠 때와 숫자 계산할 때 둘 다 + 사용
  • 형변환이 자유롭게 발생하므로 유의 "3" * "5" // => 15

4.8 산술 표현식 Arithmetic Expressions

  • *, /, %, +, –
  • NaN
    • 연산자 : 값을 합칠 때 해당 값이 객체이면 3.8.3에서 본 것처럼 toString() 또는 valueOf()로 형변환 시도
  • 비트연산자 : &, |, ^, ~, <<, >>, >>>

4.9 관계 표현식 Relational Expressions

  • ===== : 타입 변환 허용 var point = { x : 10, y: 20 };

    “x” in point // => true

    “s” in point // => false

    var data = [3, 2, 1];

    “0” in data // => true

    // 배열은 값이 들어 있는지 확인하는 것이 아니라 해당 index에 값이 있는지 확인해줌

  • instanceof : 어떤 클래스인지 확인할 때. 상속되는 모든 클래스에 대해 true

4.10 논리 표현식

  • &&, ||, !

4.11 배정 표현식 Assignment Expressions

  • =, +=, -=, *=, /=, %=, <<=, >>=, >>>=, &=, |=, ^=

4.12 평가 표현식 Evaluation Expressions

  • eval(): 하나의 아규먼트를 js 코드처럼 처리해줌.
  • ES5에서 global eval
  • IE 전용 execScript()

4.13 그 외 표현식

  • 3항 연산자(?:) : var age = birthyear > 2000 ? "yong" : "old";
  • typeof : p.82 표 참조
    • “undefined”, “object”, “boolean”, “number”, “string”, “function”, “object”, “<구현한 객체>”
    • null"object" 반환
    • 호출 가능한 객체 callable object 가 정확하게 함수function는 아님. 하지만 typeof에서는 호출 가능한 객체에 대해 “function”을 반환
  • delete
    • ES5 strict에선 못지울 것 지우면 SyntaxError
    • garbage collection이 있으므로 일일이 지워줄 필요는 없음
  • void : 프로토콜에서 사용하는데 (주. 쓰지말자.)
  • , : a=1, b=2, c=3;

짧지 않았던 3번째 챕터인데 깊은 내용은 후반부에 다룬다는 부분이 많았다. 전반적으로 살펴보는 느낌으로 읽으면 좋을 것 같다.


Chapter 3 타입1, 값, 변수 Types, Values, and Variables

이 챕터에서는 다음 세가지에 대해 심층적인 설명을 다룸.

  • 타입 : 문자열, 숫자, 객체 등 값이 어떤 형태의 자료인지를 의미
  • 값 : 3.14, “Hello World” 실제 저장되는 정보
  • 변수 : 값을 나중에 사용하기 위해 이름으로 저장해두는 공간

3.1 숫자

64비트 부동 소수점 포맷

  • 정수 문자열 : 10진수(255), 16진수(0xff), 8진수(0377)
  • 부동 소수점 문자열 : [digits][.digits][(E|e)[(+|-)]digits]
  • 계산 : +, -, *, /, % and Math object (p.33)
  • 무한대 : -Infinity, Infinity, negative 0 (but 0 == -0 => true)
  • NaN : Not-a-number value. 0으로 나누기 등 에러가 발생하는 계산에서 나타나는 값 (비교연산자 못씀. isNaN())
  • 다른 프로그래밍 언어처럼 부동 소수점 처리에 문제가 있으므로 정수 영역으로 쓸 것
  • 일자와 시간 : Date object (p.35), new Date().getTime(). 숫자처럼 비교 가능한 객체

3.2 텍스트

  • 불변(배열같이 0부터 인덱싱), 16비트, 유니코드
  • ” 또는 “”, 개행 또는 이스케이프 시 \ 사용 (p.37, 38)
  • 텍스트 메소드 p.38~
  • 정규표현식 : /hello/g.test("hello world") (챕터 10)

3.3 불린값 Boolean Values

  • true 또는 false
  • undefined, null, ``, -0, NaN, "" => false

3.4 nullundefined

  • undefined : 시스템 레벨, 예측할 수 없어서 마치 에러같은 빈 값, TypeError
  • null : 프로그램 레벨, 일반적 또는 예측 가능한 형태로의 빈 값, no object as an object
  • ==로는 같으나(false이므로) ===로는 다름

3.5 전역객체 The Global Object

JavaScript 해석기(interpreter)가 실행될 때 선언됨 (새로고침하거나.. 할 때 항상)

  • 전역 프로퍼티 : undefined, Infinity, NaN
  • 전역 함수 : isNaN(), parseInt(), eval()
  • 생성자 함수 : Date(), RegExp(), String(), Object()
  • 전역 객체 : Math, JSON

웹브라우저에서는 window, 콘솔에서는 global

3.6 레퍼 객체 Wrapper Objects

기본형 primitive value인 경우 프로퍼티를 가질 수 없으므로 그런 경우 레퍼 객체를 활용할 수 있음. OO로 개발할 때 도움. (Java의 레퍼 클래스와 비슷한듯)

var s = "test", d = new String("test");
s.len = 4, d.len = 4;
console.log(s.len, d.len); // undefined 4
  • String(), Number(), Boolean()

3.7 변하지 않는 기본형과 변하는 객체 참조 Immutable Primitive Values and Mutable Object

References

  • 기본형과 객체의 기본적인 차이인 불변성. 객체는 값이 아닌 참조가 들어있기 때문.
  • 객체를 비교하거나 복사할 때는 유의해야 함. (비교 코드 예시 p.45)

3.8 타입 변환

JavaScript에서의 타입 변환은 완전 유연

  • true로 변하는 값, false로 변하는 값

  • 타입 변환 표 p. 46

  • 비교할 때 타입 변환을 고려할지 안할지. ==, ===

  • 명시적 변환 : 레퍼 객체 또는 x + "", +x",!!x` 활용

  • 진수 변환, 정수 또는 소수 등 parsing parseInt(), (320.329).toFixed(2)

  • 3.8.3 객체를 기본형으로 변환 : valueOf() => toString() => TypeError

  • 객체-숫자 변환과 객체-텍스트 변환 두가지로 문제 발생할 수 있음 typeof(new Date() + 1) // => “string”

    typeof(new Date() – 1) // => “number”

    typeof(+new Date() + 1) // => “number”

3.9 변수 선언

  • var로 선언 : 타입이 따로 지정되지 않음.
  • 중복 선언해도 에러 발생 안함. 선언 안하면 에러남.
  • 항상 var 사용하는걸 권장. => 3.10.2

3.10 변수 스코프 Variable Scope

  • 프로그램 소스코드에서 선언한 변수를 사용할 수 있는 범위. 전역 변수는 전역 스코프를 가짐.

  • 함수 스코프와 호이스팅 Hoisting

    — C에서는 { } 기준의 block scope지만 js에서는 함수 기준의 스코프

    — 함수 중간에 var로 변수선언이 있으면 함수 실행 시 처음에 다 선언처리함 (hoisted)

  • 프로퍼티로서의 변수 : var로 선언하면 삭제 불가능한 프로퍼티로 생성

  • 스코프 체인

    — 변수 확인할 때 스코프 내에 선언되지 않은 경우 상위 스코프로 계속 이동하며 찾음. 끝까지 없으면 ReferenceError

    — 함수 내에 함수를 선언 했을 때

    with 5.7.1, 클로저의 이해 8.6 참고

  • 프로그래밍 서적에서는 흔히 type을 형으로 번역하는데 그냥 영어로 적는다. 

  • 기초적인 부분이긴 하지만 유니코드 값 비교 부분은 잘 살펴볼 필요가 있다. 꼭 자바스크립트가 아니더라도 각 언어에서 유니코드를 어떻게 처리하고 다루는지 알아두면 유용하다. 발생할 수 있는 수많은 케이스가 있는데 그 중 하나로 음악 스트리밍 서비스인 spotify가 사용자명으로 유니코드를 사용해 보안 문제가 발생했었던 사례를 들 수 있다.


    Chapter 2 어휘 구조 Lexical Structure

    어휘 구조는 프로그래밍 언어의 가장 기초적인 문법.

    2.1 캐릭터 셋

    ASCII와 Latin-1를 포함하고 있는 유니코드

    2.1.1 대소문자 구분 – 구분한다

    html은 구분 안하므로 유의

    2.1.2 공백, 줄바꿈, 양식 문자

    공백은 무시됨, 가독성을 위해 사용 (예외 2.5)

    줄바꿈은 단일 행 실행 종료로도 처리됨 (세미콜론 없어도 된다는 말)

    식별자 빼고 LTR, RTL MARK 등 양식 문자를 사용할 수 있음

    2.1.3 유니코드 탈출 문자열

    유니코드 미지원 환경을 위한 유니코드 사용

    \u + 4자리 8진수 주소

    "résumé" === "r\u00e9sum\u00e9" // => true
    

    2.1.4 표준화 Normalization

    유니코드는 같은 글자를 여러 방법으로 표기할 수 있음

    é는 유니코드 문자 하나로 표기할 수도 있고 e + ́ 로도 표시할 수 있음

    console.log("e\u0301", "\u00e9"); // => é é
    console.log("e\u0301" === "\u00e9"); // => false
    

    자바스크립트는 작성된 코드를 이미 표준화 된 것으로 보고 별도로 표준화 처리 안함

    2.2 주석

    /* 블럭주석 */
    /*
    * 블럭주석
    */
    // 인라인 주석
    

    2.3 문자열 Literals

    문자열은 데이터 값을 의미

    number, string(Chapter 3), boolean, 정규표현식(Chapter 10), null, array, object

    2.4 식별자와 예약어

    문자, _, $, 숫자

    첫글자는 숫자 못씀

    일반적으로 아스키 문자와 숫자

    유니코드 식별자도 가능

    2.4.1 예약어

    예약어는 식별자로 쓸 수 없음 – 기본 함수, 타입 등등

    예약어를 무심코 쓰더라도 예약어 썼다고 에러를 출력하므로 외울 필요는 없어 보임

    (변수명을 undefined로 한다거나 -_ 혼난다)

    2.5 선택적 세미콜론

    다른 언어처럼 각각 명령문 끝에 ; 넣음

    세미콜론 대신 줄바꿈으로 생략도 되는데 말이 안되는 줄바꿈은 알아서 인식함

    작성 의도와 다르게 실행되는 예시 – 코드의 명확성을 위해 세미콜론 꼭 쓰자

    색상을 바꿔요

    눈에 편한 색상을 골라보세요 :)

    Darkreader 플러그인으로 선택한 색상이 제대로 표시되지 않을 수 있습니다.