카테고리 없음 | 2024. 2. 29. 13:54 | /49

서론

매일 매일이 새로운 문제를 해결하는 날들의 연속이다. (삶도 실험이네 ㅎㅎㅎ)

미국에서 포닥을 시작하며 한국 핸드폰과 번호를 가져가기로 정했다. 하나의 휴대폰에 2개의 sim을 쓰거나 usim을 바꿔끼는 방법도 있지만 분명 자잘한 문제들이 생길 것 같았기에 (나중에 찾아보니 정말 많은 문제들이 생기더라. 모험을 하지 말자 ㅎ) 미국용 공기계를 새로 하나 샀고, 편의점에서 KT 바로유심을 사서 미국으로 갔다.

일주일정도 정착과정을 완료한 후, 한국 핸드폰을 슬 월 1,800원 유심으로 바꾸려고 했다. 여자친구가 셀프개통을 순조롭게 진행했다고도 했고 로밍도 다 되고 wifi도 다 되는데 무슨 문제가 있을까 싶었다.

 

요약

미국에서 KT Skylife 번호이동은 안된다. 정확히는 번호이동과 개통도 되고 문자 수신도 가능하지만 내 기기가 자신의 번호가 무엇인지 모르게 되고 발신이 제한된다. 

단, 번호이동 당일에 한하여 취소를 하면 아무일도 없었던 것처럼 돌아간다. (새로 산 유심은 버리게 되지만)

 

본론

핸드폰 개통 자체는 핸드폰이 네트워크에 물리적으로 연결되어 있지 않아도 가능하다. 무슨 말이냐면 내 USIM카드의 번호와 핸드폰 번호, 개인정보를 등록하는 것이라 WiFi 만으로도 가능하다. 안그러면 어떠한 네트워크에도 연결을 할 수 없는 공기계에서는 절대 셀프개통이 되지 않을 것. 

 

문제는 개통 완료후 다음 단계가 기기가 번호를 받아오는 과정이다. NAMing, 네밍이라는 과정을 거쳐야 하는데, 이 과정은 국내 네트워크에서 진행이 되어야만 한다 (고객센터 확인). 일부 SK 알뜰폰류에서는 된다는 말을 본적이 있지만 추가 확인이 필요하다.

 

하나 웃긴것은 개통완료 후에 통신사와 로밍서비스를 제공해주는 해외통신사는 내 핸드폰이 누구인지 알아 문자 수신, 모바일 데이터 사용이 가능하다. 그래서 필자도 재부팅 후 NAMing 과정이 진행되는 동안 문자가 수신이 되는 것을 보고 아무런 문제가 없을 줄 알았다. 그러나 번호등록과정이 끝나지 않고 자꾸 에러를 내뱉으며 다음 단계로 넘어가지 않았다.

불안의 연속.. 결국 끝끝내 번호등록은 성공하지 못했다.

 

사실, 한국 서비스가 필요한 이유는 단 두 가지다. 1)나중에 돌아갔을 때 내 주민등록번호보다도 중요한 핸드폰 번호를 킵할 수 있을 것. 2) 본인인증을 할 수 있을 것. 때문에 문자 수신도 잘 되는데 언젠가 한국에 방문할때 번호를 받아오면 되지 않나도 생각을 했었다. 그러나 가장 빨리 나가도 3개월 뒤의 일이었기 때문에 일단 고객센터에 문의를 넣었다. 일부 후기에서 고객센터에서 강제 등록을 해서 해결한 케이스도 있다고 했다. (그런데 아마 이번처럼 핸드폰이 자기 번호를 못 받아오는 케이스는 해당되지 않을 것 같다.

 

여자친구의 도움으로 KT Skylife에 전화를 해서 알아본 결과 해외에서 번호이동은 불가능하며, 번호이동 당일에는 Ctrl+Z가 가능하다는 정보를 받았다. 

 

KT Skylife의 경우 해외에서 고객센터 전화가 다 막혀있고, 아래 번호만 작동을 한다. (도대체 어디에 있는 번호지? 왜 못찾았지..)

 

+82-2-2620-5000

 

아래로 전화를 걸어 당일 번호이동 취소를 하면 일단 이전 서비스로 복구가 가능하다. 대신 한번 등록기록이 있기에 새로운 USIM은 사용하지 못한다고 한다.

 

모두 번호이동은 국내에 있을 때 하시길....

Posted by Knowblesse

서론

절망적인 독서량을 고려해서 자기개발서류는 가능하면 시작조차 하지 않는다. 정말 유명한 책이 나왔다고 하더라도 실제 주장하는 바는 2%, 나머지는 왜 이러한 방법이 좋은지를 설명하는 것에 그치기에 시간을 들여 다 읽을 필요는 없다고 생각한다. 주장만 납득이 된다면 나머지를 전혀 읽지 않아도 아는 것에는 지장이 없을 터이니. 문제는 이를 실천하는 쪽이겠지만 말이다.

 대학원생 생활을 하면서 본인의 장기프로젝트 진행 능력이 거의 0에 가까움을 뼈저리게 깨달았다. 성과를 평가할 확실한 metric 이 없고, 중간 평가도 존재하지 않으며 (실험을 배우는 단계에서는 논문도 좋은 metric으로는 기능하지 않는다) , 연구란 것이 어느 수준에서 불완전한 상태로라도 마무리 하지 않으면 끝이 나지 않기에 대학원생이야 말로 뚜렷한 장기 계획 능력이 필수이다. 혹 과거로 돌아가 다시 석박통합과정을 처음부터 시작하게 된다면 온갖 시행착오를 겪더라도 무엇보다 이 능력을 갈고 닦을 것이다.

 때문에 조금이나마 문서화된 도움을 얻고자 이 책을 읽게 되었다. 일부는 내가 이미 시험삼아 실행했다가 생각보다 엄청난 효과에 놀라고 있던 것이었고, 일부는 당연하다고 여기는 것들, 또 일부는 효과가 있을까 의문이 생기지만 해볼만한 가치가 있는 방법들이었다. 이 중 당연하다고 여기는 파트들은 빠른 속도로 스킵하면서 넘겼기에 독서록에 기록한 부분이 그리 많지 않다. 몇몇 내용들을 내 생각들을 추가해서 아래 정리해본다.

 

계획 세우기

버킷리스트와, 마스터 플랜, 액션 플랜, To Do list를 구분하자.

버킷리스트는 인생 단위로 봐서 이루고 싶은 꿈들을 현실성과는 상관 없이 정한다. 단, 보통 버킷리스트에 쓰는 '스카이 다이빙 해보기' 마냥 가벼운 것들이 아니라 삶의 목적이 될만한 것들로 채운다.

다음은 10-20년 단위의 마스터 플랜을 정한다. 특정 직업을 얻기, 무언가 만들어 내기 처럼 큰, 하지만 현실성이 있는 목표들이 여기에 해당된다.

액션 플랜은 마스터 플랜을 달성하기 위한 길게는 1년 단위의 더 세부적인 목표이다. 논문 쓰기, 책 출간하기 등이 여기 해당된다.

To Do list는 이를 달성하기 위해 오늘 끝낼 수 있는 일들의 목록이다. 논문의 결론 파트 한 문단 작성 등이 여기 해당 된다.

 

버킷리스트와 마스터 플랜의 조건

종이에 기록하는 것이 도움이 된다. 왜 인지는 모르겠지만, 감정적인 정리가 필요할 때에는 꼭 종이와 만년필을 꺼내든다. 그렇게나 글을 쓰기 싫어하는 학생이었는데 아직까지 생각 정리를 함에 있어서 종이와 펜 이상의 것을 찾지 못했다.

 

To Do List의 조건

SMART 원칙이라는 것이 괜찮은 기준인 것 같다. Specific, Measurable, Attainable, Relevant, Time-bound

이 중 specific과 time-bound가 내게 있어서 가장 어려운 파트다. 생각보다 스스로 큰 덩이의 일을 할 수 있다고 생각하고 너무 크게 To Do List를 잡으며, 여기 걸릴 시간도 너무 낙관적으로 짧게 잡는다. 물론 호프스태터의 법칙이 있듯이 비관적으로 생각하여 길게 잡아도 이보다 오래 걸리겠지만... 메타인지 훈련을 위해서라도 꼭 To Do List 안에는 완성에 필요한 시간을 적어두는 것이 좋다.

 

Specific To Do List

링피트를 하는 습관을 만드려고 시도했던 것 중에 하나가 To Do List를 아주 단순하게 만드는 것이었다. "운동하기", "매일 링피트 30분을 하기" 이런 것이 아니라 무려 "하루에 한번 닌텐도 스위치 켜기" 였다. '이걸 못할 수는 없지' 수준으로 To Do 를 만들면 양심에 찔려서라도 그 뒤를 할 수 있게 되더라. To Do 를 잘 못지키게 되는 것 같으면 더 세세한 단위로 쪼갤 필요가 있다. 아래는 기억해두면 좋을 말들이라 적어둔다.

소설을 쓰는 것은 밤에 자동차를 운전하는 것과 같다. 당신은 차의 헤드라이트가 비춰주는 데까지만 볼 수 있을 뿐이다. 헤드라이트가 비춰주는 곳까지 달리다 보면 목적지까지 갈 수 있다. - E. L. 닥터로우
You solve one probelm and you solve the next one, and then the next. And If you solve enough probelms, you get to come home. - Martian

 

역산 스케줄링

이것도 어려워 하는 파트 중 하나이다. 특히 상황이 절망적이면 큰 그림을 보려고 하지 않고 자꾸 '그냥 지금 열심히 하자' 수준에서 일을 수행한다. 꼭 액션 플랜을 기억하며 Dead Line을 정하고 이를 기반으로 To Do List를 짜야한다. 짜야 하는데..

 

전날 밤에 내일 할 일 생각하고 써두기

시험 삼아 시도했다가 엄청난 효과를 봤었는데, 이 책에도 비슷한 내용이 짧게 소개되어 있었다. 전형적인 야행성 인간이라 그런지 아침에는 집중도 안되고 큰 범위의 생각이 어렵다. 때문에 전날에 멍청해진 오전의 자신을 위해 무지성으로 해야할 일들 리스트를 적고, 다음날 졸린 나는 하라는 대로 일을 하기 시작했는데, 오전 시간의 효율을 정말 높여주었다. 한가지 특이점은 앱에 기록해두면 잘 쓰지 않고 꼭 종이에 기록해서 아침에 학교에 가며 가져가도록 하는 쪽이 효과가 좋았다는 점이다.

계획 실행하기

계획 실행의 1/3 정도는 계획 수정이다.

무엇을 하더라도 결국 계획은 실제 실행 내역과 어그러지게 된다. 그렇게 실제 실행 내역을 보고 계획을 수정하는 일을 계혹 하다보면 애당초 맞지도 않을 계획을 왜 짰나 싶을 정도로 낙담하게 된다. 이것은 과연 효과가 있을 지 모르겠지만, 현실을 부정하지 않고 계속 계획 수정을 하는 것을 지속해봐야 겠다.

 

결론

계층적인, 그리고 시간을 정할 수 있는 할일 나누기가 관건인것 같은데 최근 마음에 드는 사이트를 하나 찾았다. 2주 정도 사용을 해보고 마음에 들어 별 기능도 없는 사이트지만 한달치를 일단 결제해 사용해보기로 했다. 사이트는 아래와 같다.

http://quire.io/

 

Quire - The ultimate project management software for teams

The best project management software that actually helps remote teams plan, collaborate, get focused and get things done.

quire.io

To Do List를 계층적으로 만들 수 있고, 전체적인 일의 흐름도 볼 수 있다. Timeline view가 유료 전용이라 아쉽지만, 일단 대체제를 찾기 전까지는 이 사이트를 사용해볼 생각이다.

Posted by Knowblesse

 

  제목과 표지만으로 책장에 꼽아두었더니 가치를 충분히 수행했던 책이다. 간접적인 방법으로 주변 사람의 멍청함을 당당하게 욕하는 통쾌함과, 주변인들의 다양한 반응을 구경하는 재미, (’이거 나 보라고 꼽아둔거지!’, ‘너도 멍청할 때가 있잖아!’, ‘아니야 이거 분명 저 사람을 멍청하다고 말하고 싶었던 것일꺼야’) 그리고 가볍게 읽을 수 있는 내용의 셋을 챙길 수 있다. 물리적인 책의 존재로 오는 만족감이 있었기 때문에 전자책인 경우 1/5점, 종이책인 경우 4/5점을 주고 싶다.

 

  전반적인 내용은 다양한 저널리스트, 심리학자, 칼럼리스트, 작가 들에게 멍청함에 대한 글을 요청하거나 멍청함에 대한 인터뷰를 요청하고 그 내용을 모은 모음집이다. 제목 때문에 멍청함에 대한 심도깊은 탐구가 있을 것이라 기대했는데 그렇지는 않았다. 생각해보니 내게 ‘인간의 멍청함’을 연구해보라고 하면 도대체 어디서부터 시작해야할지 감도 못잡을 것 같다. 때문에 내용이 난잡하게 얽혀있고, 멍청함과 관련된 뇌 영역을 이야기하는 글 부터 육식의 멍청함을 이야기하는 글까지 다양한 내용이 통일성 없이 섞여있다. ‘책’이라기 보다는 ‘멍청함을 주제로한 잡지 특별호’ 의 느낌이 더 가까울지도 모르겠다. 

 

  프랑스 인들의 글의 비중이 상당히 높았는데, 이들의 문화적 특성으로 보이는 모습을 얼핏 볼 수 있었다. 바로 얼마전 미국인이 쓴 조금은 딱딱한 내용의 책을 읽어서인지 유독 많은 글들이 그림, 소설 등으로부터 내용전개를 시작하는 것처럼 보였다. 안타깝게도 대부분의 내용은 멍청함을 다양한 방식으로 욕하고 ‘그들을 구제해줄 수 없으니 피해라’ 식의 결론을 내리는 터라, 소개된 그림이나 책들을 찾아보는 쪽이 더 즐거웠던것 같다. 심리학과에 있으면서 이름을 한번쯤은 다 들어본 유명한 교수들도 인터뷰가 있었는데, 과학자답게 질문이 충분히 세부적이지 않으면 “그럴 수도 있고 아닐 수도 있죠. 이런 결과도 있고 아닌 결과도 존재합니다.” 식이었다. 앞서 말했듯이 잡지를 읽는 느낌으로 읽어야 온전히 책을 즐길 수 있을것 같다. 

 

  그나마 진지함이 느껴졌던 글은 세바스티아 디게 교수의 글로 멍청함의 근원을 철학자 해리 프랑크푸르트의 말을 인용하며 멍청함은 “진실에 대한 관심부재”에 기인한다고 이야기 했다. 내가 생각해도 지능 부족으로 진실을 이해할 여력이 되지 않는 것을 넘어, 진실 그 자체에 관심이 없거나, 진실 여부와는 상관없이 다른 것에 목적이 있는 경우에는 분명 행동이 멍청해지는 것 같다. 이렇게 전문성이나 지능과는 상관 없이 “관심의 부재”로 인해 누구나 멍청해 질 수 있기에, 대다수의 글들에서 ‘누구나 다 멍청해질 수 있음을 잊지 말고 자기 비판적인 사고를 해야한다.’ 고 결론을 내린다. 안타깝게도 어떤 글도 미래에는 멍청함이 줄어들 것이라는 낙관적인 예상을 하지 않는다. 인류는 결국 멍청함과 함께 해야만 하는 존재인가.

 

Posted by Knowblesse

서론

종교를 가지고 있지만 불가지론을 지향하는 과학자로서 (제목과는 다르게 아이러니 하게도) “종교 서적”으로 분류된 이 책을 스스로 읽을 이유는 전혀 없었다. 단지 최근에 읽은 Ray Dalio의 <Principle>에서 이 책을 인용했다는 점과, 종교 최대 지도자가 종교를 “넘어”라는 표현을 썼다는 점이 신경쓰여서 책을 잡아들게 되었다. 전체적으로는 내가 자기개발서에 기대하는 수준을 넘기지는 못했으나, 여러가지 생각할 점들을 주었다는 것과, 얇은 책 두께 때문에 읽은 시간이 아깝지는 않았다.

 

종교의 몰락.

종교에는 신에 의해 진실이 보장된 도덕이 있고, 사후세계에서의 심판이든 다음 생에서의 삶이든 옳음을 강요하는 강력한 힘이 존재한다. 그러나 세계화로 종교간의 의견 충돌이 보이기 시작했고, 종교들이 제시하던 도덕 또한 ‘상대적 진실’에 불과하다는 점이 드러나면서 종교는 예전처럼 힘을 쓰지 못하게 되었다. 또한 순기능에 비해서 점점 비대해지는 종교에 반해서 생겨난 과학의 등장으로 종교의 가치는 점점 하향세를 걷고 있다. 확실하지 않으면 존재 자체를 부정하는 과학을 하는 하는 내 입장에서 이러한 변화는 자연스러운 것이며, 종교는 결국 일부 쇠퇴하리라고 생각한다.

 

마음챙김은 그러면 이제 누가 어떻게?

문제는 종교의 몰락이 아니라 종교가 원래 수행하던 일을 어떤 객체가 이어서 수행을 하냐는 것이다. ‘마음’이든, ‘내적 가치’든, ‘자비’든 인간의 온전한 정신상태는 인류에게 단 한번도 중요하지 않았던 적이 없었다. 단지 과거에는 종교가 이러한 일들의 일부를 담당하고 있었으나 종교의 몰락과 함께 마음챙김(이 단어를 매우 싫어하지만 적절한 단어를 찾지 못해서 그대로 사용한다)을 담당할 무언가가 사라지고 있다. 이 책에서 마음챙김의 중요성을 첫 챕터에서 지적을 하는데, 달라이 라마도 종교의 한계를 인정하지만, 그렇다고 종교가 원래 해오던 일들까지도 함께 무시해서는 안된다고 이야기한다. 때문에 이 책의 제목이 종교를 “넘어” 인 것이다. 역자의 후기를 읽어보면 달라이 라마가 어느날 갑자기 이러한 이야기를 한 것이 아니라 꽤 오래전부터 종교 그 이상의 가치를 지켜야한다고 역설해온 것으로 보인다. 어쩌면 불교가 종교의 많은 구성 요소를 자기 수행과 온전한 정신상태에 초점을 두고 있기에 가장 먼저 종교의 부재로 발생하는 문제점들을 인식한 것이 아닌가 생각한다. 개신교에 익숙한 나로서는 단 한번도 종교의 약체화가 인류에게 이러한 영향을 미치리라고 생각을 해본 적이 없었다. 확실히 다양한 가치가 자유와 다양성이라는 이름하에 수용이 강제되고 있고, 더이상 흑백논리적 도덕적 판단이 어려워지는 현재에 있어서 설사 그것이 상대적 진실이라고 하더라도 기준점이 되어줄 무언가가 절실히 필요하다. 무엇이 옳고, 우리가 어떤 정신을 가지고 어디에 더 중점을 두어야 하는지를 생각하는 것이 더 중요해졌다.

 

달라이 라마의 대책

그렇다면 어떤 객체가 기존의 종교가 맡아서 하던 마음챙김의 역할을 대신 해줄 수 있을까. 우리가 나아가야할 방향을 확실히 제시해주고, 좀 더 일관되고 올곧은 현세적 도덕관을 제시해주며, 한 사람 한 사람을 위로해줄 수 있는 무언가가 나타난다면 그 형태가 어떻든 간에 21세기에 가장 강력한 ‘종교’가 될 것임이 분명하다. 책의 2/3을 할애하는 부분은 달라이 라마의 대책이다. 안타깝게도 달라이 라마 본인에게 가장 익숙한 불교적 방법, 스스로의 수행과 명상을 통한 자비에 기반한 마음챙김을 해법으로 제시한다. 인류를 하나로 묶어주는 강력한 대상이 없어지는 만큼 ‘각자도생으로 자기 마음은 자기가 챙기자’의 논리가 틀린 것은 아니겠으나, 뻔한 자기 개발서의 흐름을 벗어나지 못했다는 생각에 아쉬움이 앞섰다. 아무리 자애로운 마음이 무엇인지, 절제와 덕이 무엇인지를 설명해도 달라이 라마 본인의 말처럼 이는 ‘지식의 문제가 아니라 행동의 문제’이다. 책을 통해 조금이나마 방향을 잡아주지 않을까 하는 기대를 했으나, 스스로 깨닫고 행동하지 않으면 알 수 없는 내용들에 대한 설명만이 마지막 페이지까지 이어졌다. 그나마 다루는 내용의 무게에 비해 얇은 두께로 그가 이런 한계를 인지하고 최대한 ‘절제’를 해서 글을 썼다는 점을 느낄 수 있었다.

 

총평

전혀 관심을 주지 않을 법한 주제와, 다른 종교의 사고관을 간접적으로 나마 접할 수 있었다는 점에서 충분히 읽을 만한 책이었다. 특히 종교의 부재로 일어나는 일에 대해서는 단 한번도 생각을 해본 적이 없었으나, 이 책을 읽고 달라이 라마의 염려에 확실히 동의할 수 있었다. 그리고 내가 접해본 종교인들은 본인의 ‘옳음’에 심취되어 있거나, 다른 분야에 대한 무지로 헛소리를 하는 경우가 대부분이었는데 달라이 라마는 확실히 다른 것 같다. 열린 마음으로 다양한 학문을 탐구하고, 자신의 종교와의 공통점을 찾고, 더 나아가서 자신의 종교 그 이상의 메타적인 시각에서 인류를 조망하는 것이 확실히 느껴졌다.

Posted by Knowblesse

2D Gaussian Convolution filter

2D space에서 image smoothing, noise 제거 등을 위해서 Gaussian filter를 적용할 때가 있다.

Matlab에는 워낙 다양한 toolbox들이 있어서 간혹 유사한 기능을 하는 함수들이 존재하기도 하는데, 때문에 같은 기능을 다양한 방식으로 구현을 할 수 있다.

오늘 imgaussfilt 함수와 mvnpdf 함수를 사용해서 Gaussian filter를 적용을 하다가 몇가지 차이점과 유의점을 발견하여 이곳에 적는다.

 

Original Image

코드 설명에 사용할 Original Image를 만드는 코드이다.

100 x 100 이미지에 (50, 50) 과 (20, 50) 의 pixel만 1의 값이 들어가있다.

originalImage = zeros(100,100);
originalImage(50, 50) = 1;
originalImage(20, 50) = 1;

Method 1: imgaussfilt

함수 개발 목적 자체가 2D 이미지에 Gaussian filter를 적용하기 위한 것이기 때문에 가장 간단하게 원하는 목적을 달성할 수 있다.

figure(1);
clf;
sigma = 1;
fimage1 = imgaussfilt(originalImage, sigma, 'FilterSize', 101);
imagesc(fimage1);

여기서 중요한 것은, 'FilterSize' Name-Value pair인데, 이 값을 입력하지 않거나, 작은 값으로 설정하는 경우 convolve 하는 kernel의 크기가 작아서 빠르지만 조금은 부정확한 결과가 나올 수 있다.

참고로 fimage1의 합은 2가 된다.

 

Method 2: mvnpdf로 kernel을 만든 뒤 conv2

직접 2D Gaussian kernel을 만든 뒤에 원본이미지와 conv2 함수를 사용해서 적용하는 방식이다.

[X,Y] = meshgrid(1:101, 1:101);
X = X(:);
Y = Y(:);
mu = 51;
sigma = [1, 0; 0, 1];
kernel = reshape(mvnpdf([X,Y],mu,sigma),101, 101);

fimage2 = conv2(originalImage, kernel, 'same');

figure(2);
imagesc(fimage2);

51의 값은 101 크기의 kernel의 중간값이다.

 

위 결과는 imgaussfilt의 결과와 정확히 일치한다.

 

 

 

함수 개발 목적 자체가 2D 이미지에 Gaussian filter를 적용하기 위한 것이기 때문에 가장 간단하게 원하는 목적을 달성할 수 있다.

Posted by Knowblesse

들어가며

  안전한 보안을 위한 좋은 방법 중 하나는 Two Factor Authentication (2FA)를 계정에 설정해두는 것이다. 사이드마다 다른 비빌먼호 생성, 주기적인 비밀번호 변경보다 훨씬 쉽게 보안 수준을 높게 유지할 수 있다. 그런데 github에 2FA 인증을 설정해두면 서드파티 앱이나 shell에 있는 git 에서 remote repository (remote)에 접근을 하기 어려워진다. 또한 지난 2021년 8월 13일부로 github 계정을 사용한 remote 접근이 완전히 차단되어서 더이상 단순히 비밀번호를 입력해서 접근하기가 어려워졌다. 때문에 아래와 같은 방법을 통해 remote에 접근 할 수 있다.

 

1. Token 발급 : sub-password의 역할을 하는 token을 발급받아 비밀번호 대용으로 쓰는 방식으로, 각 token 마다 유효기간, 권한등을 설정할 수 있다. 기존의 github 계정 비빌번호를 사용하는 방법을 완전히 대체 가능하다.

2. OAuth : Open Authentication. 로컬 git, 서트파티앱에 github 로그인 정보를 제공하지 않고 github에 연결하게 해주는 인증방식이다.

3. SSH : 이 글에서 다룰 내용으로 한번 설정만 제대로 한다면 편리하고 보안성도 좋다.

 

설정 난이도만 생각하면 Token을 발급받는 방법이 가장 쉽다. Token을 발급받아서 필요한 권한을 준 뒤, 어딘가에 적어두고 remote를 연결할 때 사용하면 된다. 그러나 더 높은 보안과 편리성을 위해서는 SSH를 사용하는 것을 추천한다.


SSH 연결 특징

  SSH는 Secure SHell의 약자이며 구체적인 SSH 통신 방법은 여기에서 다루지 않겠다. 대신, SSH 를 사용하면 어떤 것들이 필요하고, HTTPS를 사용하는 방식과는 어떻게 다른지 비교를 하겠다.

 

사용자 이름을 knowblesse, repository 이름을 myrepo라고 했을 때 HTTPS와 SSH에서 필요한 것은 아래와 같다.

 

  HTTPS SSH
remote repository 주소 https://github.com/knowblesse/myrepo git@github.com:knowblesse/myrepo.git
ID knowblesse@gmail.com X
PSW (Token) ghp_********* X
Public Key / Private Key X SHA256:e/***************
(옵션) credential 저장 git config credential.helper store
git config --global user.name Knowblesse
git config --global user.email knowblesse@gmail.com
eval "$(ssh-agent -s)"
ssh-add

 

차이점 1 : remote 주소

SSH는 인증방식이 아니라 (보안 인증 프로토콜을 포함하고 있는) 통신 방식이다. 때문에 일반적으로 알고 있는 https로 시작하는 주소가 아닌 git@github.com 으로 시작하는 주소를 입력해야한다.

차이점 2 : 인증

SSH에서는 public keyprivate key, 두 가지 파일을 사용하는 공개키 암호화 방식을 기본으로 택하고 있다. 처음에 key 생성기를 사용해서 서로 매치가 되는 public key와 private key를 생성하고, 인증을 원하는 곳에 public key를 제공하면 이후 접속시 private key를 이용해서 인증받을 수 있다.

차이점 3 : Credential 저장

token을 사용하는 HTTPS 방식에서는 credential.helper를 사용해서 token을 저장해둘 수 있다. SSH에서는 ssh-agent를 사용해 credential을 저장한다.


SSH 연결 설정

SSH 연결 설정 순서는 다음과 같다.

 

1. SSH 키가 없는 경우 : SSH 키 생성

2. Github에 SSH 등록

3. local repository에 SSH 기반 remote 주소 등록

4. ssh-agent 셋업 및 연결 방법


1. SSH 키 생성

    SSH 키 생성은 다양한 프로그램이 지원하고 있는데, Window와 Linux 모두 기본으로 설치되어있는 ssh-keygen을 사용하는 방법이 가장 보편적이다.

 

먼저 기존에 생성해둔 ssh키가 없는지 간단히 확인한다.

Window ("C:\Users\Knowblesse") 와 Linux ("\home\Knowblesse") 의 유저폴더 안에 ".ssh" 라는 명칭의 폴더 안에 .pub로 끝나는 파일이 있는지 확인해보면 된다. 키 생성을 한번도 안한경우 폴더 자체가 없을 것이다.

 

Window cmd나 Linux shell에서 다음과 같이 ssh-keygen을 실행하자.

> ssh-agent -t ed25519

ssh-agent는 기본 옵션으로 rsa key pair를 만드는데 최신 방식은 ed25519라고 한다. 기본 옵션으로 키를 생성하려면 "ssh-agent"만 실행하면 된다.

 

이후 파일을 저장할 위치를 묻는데 default 위치를 그대로 사용해야지, 다른 곳에 저장을 했다가는 추가로 key 위치를 입력해주는 작업을 해야한다. 가능하면 ~/.ssh 폴더 안에 생성하도록 그대로 두자.

> ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\Knowblesse/.ssh/id_ed25519):

다음으로는 password를 묻는다.

> ssh-keygen -t ed25519
Generating public/private ed25519 key pair.
Enter file in which to save the key (C:\Users\Knowblesse/.ssh/id_ed25519):
Enter passphrase (empty for nopassphrase):

이 passphrase는 private key를 한번 더 암호화 하는데에 사용된다. private key에도 암호를 걸어두는 것을 적극 권장한다.

Q. 왜 passphrase를 설정하나요?
A. 행여나 private key가 노출되었을때를 방지하기 위함입니다. 실질적인 보안은 사실 public/private key 쌍이 담당하고 있기에 passphrase를 복잡하게 설정할 필요는 없습니다. 때문에 명칭도 password가 아니라 phrase 입니다.

 

private key에 암호설정 과정을 마치면, 아래와 같은 출력 메시지 후에 public key와 private key가 생성된다.

예시 화면

.pub 확장자를 가진 파일이 public key, 아무런 확장자가 없는 파일이 private key 이다.

생성된 두 파일은 텍스트 편집기로 열 수 있는 plain text 파일이며 private key는 절대 인터넷 상에 노출이 되면 안된다.

 

Q. 어차피 private key는 public key와 인증을 거치는 과정 중에서 인터넷 상에 노출되지 않나요?
A. SSH 인증에 사용하는 비대칭 공개키 암호화 방식은 1) 서버가 랜덤한 메시지를 호스트로 보내고, 2) 이를 받은 호스트는 private key로 암호화를 해서 다시 서버에게 보내고, 3) 암호화된 메시지를 받은 서버가 자신이 가지고 있는 public key를 사용해 복호화 한 뒤, 원래 보냈던 메시지와 비교하는 방식입니다. 때문에 private key는 인터넷 상에 절대 노출되지 않습니다.

2. Github에 SSH 등록

    생성한 public키는 Github에 등록할 수 있다.

 

 

새로운 SSH 키 등록 버튼을 누른 뒤, 텍스트 편집기로 .pub 파일을 열어서 내용을 붙여넣으면 된다.

public key 입력

3. local repository에 SSH 기반 remote 주소 등록

    여기서부터 살짝 까다로워 진다.

3-1. 처음 repository를 생성하는 경우 (clone 하는 경우)

https 기반 주소가 아니라 아래처럼 SSH 기반 주소를 입력하면 끝난다.

> git clone git@github.com/knowblesse/myrepo

 

3-2. 기존에 생성된 repository를 변경하는 경우

remote에 연결된 url 을 SSH 형식으로 수정하는 작업이 필요하다.

> git remote
origin

먼저 git remote 커맨드로 어떤 remote 와 연결되어있는지 확인한다. 위의 예시에서는 origin만 연결되어 있다.

 

추가로 아래 커맨드를 사용하면 origin의 url을 볼 수 있다.

> git remote get-url origin
https://github.com/knowblesse/myrepo.git

url의 변경은 set-url을 사용하면 된다.

> git remote set-url origin git@github.com:knowblesse/myrepo.git

형식에 주의하자

git@github.com:knowblesse/myrepo.git

Q. 여러 개의 SSH key를 사용할 경우에는 어떻게 하죠? 어떤 key를 쓰라고 지정할 수는 없나요?
A. ~/.ssh 폴더 안에 config 파일을 만들어서 어느 사이트에 연결할 때 어떤 key 파일을 사용할지 정해줄 수 있습니다. 이 방법 외의 다른 방법도 있지만, git command 안에서 "어떤 key 파일을 써라" 라고 명시적으로 알려주는 방식은 없는 것으로 알고 있습니다. config 파일을 생성하는 방법은 아래 접은 글을 참고해 주세요.
더보기

config 파일 만드는 법

open SSH 에서 사용하는 config 파일은 여러 요소로 구성되어 있으나, 복수의 key를 사용하기 위한 목적이면 Host, HostName, IdentityFile 3개의 키워드만 사용하면 된다. 구체적인 작성법은 다음 사이트를 참고하자.

https://www.ssh.com/academy/ssh/config

 

SSH config file syntax and how-tos for configuring the OpenSSH client

SSH config file syntax and how-tos for configuring the OpenSSH client

www.ssh.com

config 파일을 사용해서 복수의 key를 사용하는 원리는 Host의 alias 생성이 가능하다는 점을 이용하는 것이다.

git@github.com:knowblesse/myrepo.git

 

위의 주소에서 git 은 사용자명(knowblesse가 아니다!), github.com 은 Host의 위치, knowblesse/myrepo.git은 호스트 서버 내의 내 remote의 위치이다(정확히는 remote 정보를 담고 있는 git 파일).

 

중요한 점은 github.com 이 실제 주소가 아니라 Host의 이름 이라는 것인데 open SSH는 config 파일에서 해당 이름을 가진 Host가 없으면 이를 실제 Host 주소로 사용해서 실제 github.com과 통신을 시작한다.

 

반면, Host를 지정해주고 실제 Host의 주소를 config 파일에 명시하면 다른 형태로 접속이 가능하다.

Host myhub
	HostName github.com

예를들어, 위와 같이 config 파일을 설정해주면 이후 github.com을 입력하지 않고 단순히 아래처럼 입력할 수 있다.

git@myhub:knowblesse/myrepo.git

주의점은 HostName이 alias가 아니라 Host가 alias이다. HostName은 실제 주소다!

 

또한 config 파일에서는 각 host 마다 사용할 IdentityFile의 위치를 지정할 수 있다.

 

여기까지 설명을 하면 이미 눈치를 챘을 것이다. IdentityFile만 다르게 지정한 alias를 여러개 만들어 두면 remote의 set-url을 진행할 때 사용하는 alias에 따라 다른 key 파일을 사용하도록 할 수 있다.

 

다음은 account1_private 과 account2_private key 파일을 사용하는 config 파일의 예시이다.

#keyfile 1
Host account1
         HostName github.com
         IdentityFile ~/.ssh/account1_private
#keyfile 2
Host account2
         HostName github.com
         IdentityFile ~/.ssh/account2_private

이후 git remote set-url 함수를 사용해서 repo 마다 remote의 주소를 설정해주어야 한다.

예를 들어 account1_private key 파일을 repo1에 사용하려면 아래와 같이 url을 변경한다.

> git remote set-url origin git@account1:knowblesse/repo1.git

4. ssh-agent 셋업 및 연결 방법

    여기까지 설정을 완료했고, key 파일이 기본 위치인 ~/.ssh 안에 들어있다면 정상적으로 remote와 연결이 될 것이다. 그러나 매번 passphrase를 물어본다는 단점이 있다. 이를 해결하는 방법은 ssh-agent를 사용해서 잠시동안 passphrase를 저장해두는 것이다. 이 경우 한동안 passphrase 없이 SSH 통신을 할 수 있다. 마치 credential.helper 를 사용해서 token을 저장해두는 것과 동일하다.

 

순서는 두 과정을 거치는데, 1)ssh-agent를 실행하고, 2) ssh-add를 통한 key 등록이다.

Linux

> eval `ssh-agent -s`
Agent pid 12345
> ssh-add

Windows (PowerShell)

> Set-Service ssh-agent -StartupType Manual
> Start-Service ssh-agent
> ssh-add
Q. 왜 그냥 ssh-gent를 실행하면 안되고 꼭 eval 함수 사용해서 실행해야하죠?
A. eval을 쓰지 않고 ssh-agent 명령을 실행해도 ssh-agent 프로세스는 정상적으로 시작된다. 그런데 그 이후에 ssh-add가 정상작동하지 않는다. 그 이유는 ssh-add가 SSH 소켓에 대한 정보를 필요로 하기 때문이다.

ssh-agent 명령을 실행하면 다음과 같은 출력이 나온다.

> ssh-agent -s
SSH_AUTH_SOCK=/tmp/ssh-XXXXXXmj93qJ/agent.36991;
export SSH_AUTH_SOCK;
SSH_AGENT_PID=36992;
export SSH_AGENT_PID;
pid 36992;

눈치 챘겠지만, ssh-agent 명령은 ssh-add 에게 매번 긴 SSH_AUTH_SOCK에 해당하는 값을 전달해주지 않도록 SSH 소켓과 ssh-agent 관련 정보를 bash 코드로 출력한다. 때문에 출력 텍스트를 그대로 코드처럼 실행시키는 eval 함수를 사용하여 ssh-agent를 실행하면, ssh-agent의 출력결과인 소켓과 pid 설정을 자동으로 할 수 있다!

때문에 굳이 eval을 쓰고 싶지 않다면, ssh-agent를 돌려서 나온 소켓과 pid 정보를 아래와 같이 변수로 입력해주면 된다.

> SSH_AUTH_SOCK=/tmp/ssh-XXXXXXmj93qJ/agent.36991
> SSH_AGENT_PID=36992
> ssh-add
Q. 왜 윈도우에서는 서비스 시작 방법을 바꾸나요?
A. Startup Type 이 Manual이 아니면 수동 시작이 안되는 것 같다. 윈도우는 UI가 잘 되어 있으니 수동시작을 하지말고 시작시 자동 시작을 하게 하면 더 편할 것 같다.

마치며

SSH 설정이 단순하지는 않지만, 한번 설정만 해두면 높은 보안수준을 유지하며 쉽게 데이터 공유가 가능하다.

Posted by Knowblesse

 

심리학과 답게 진행한 Negative Punishment. 효과는 없었다.

사용 후 꼭 레버를 올려주세요

 연구실에 사비로 캡슐 커피머신을 비치해두었는데, 공유지의 비극으로 관리의 문제가 심각해진 적이 있었다. 캡슐을 사용하고 레버를 올리지 않는 경우 캡슐이 그대로 기기안에 남는데, 기온이 높은 여름에는 따뜻하고 눅눅한, 곰팡이가 번식하기에 최적인 조건이 만들어진다. 특히 금요일 오후즈음 이런 상황이 발생하면 주말내내 커피머신은 곰팡이 배양기로써 그 역할을 충실히 수행한다. 월요일 아침에 커피를 마시기 위해 레버를 올리는 순간, 떨어지는 캡슐에 핀 곰팡이를 보게되면 결국 기기 분해 세척은 주인인 내 몫이 되었다.

 

 다들 범행(?)을 부인하는 통에 전체 공지도 돌리고 사진처럼 Negative Punishment도 해봤으나 인간은 쉽사리 바뀌지 않는법. 결국 Raspberry Pi를 사용해 커피 머신 사용 중 동영상을 촬영하고, 2분내로 레버를 올린 경우 동영상은 삭제, 레버를 올리지 않은 경우는 동영상을 저장한 뒤, 내게 이메일로 노티를 주는 장치를 만들었다. (코드)

 

GitHub - knowblesse/BlueberryPi

Contribute to knowblesse/BlueberryPi development by creating an account on GitHub.

github.com

 

 원래 계획은 범인의 커피 내리는 영상을 (이게 생각보다 우스꽝스럽다) 반복 재생시켜두려고 했으나, 매우 안타깝게도 높으신 분의 반복범행임이 밝혀져서 깔끔히 포기했다. 10개의 범행장면을 모은 뒤 '두근두근 범인 공개 상영회'를 연 것으로 만족했으며, 그냥 스스로 자주 확인을 하기로 했다.

 

코드 다 돌아가면 알려줘!

 앞서 언급한 프로젝트를 진행하던 중 처음 겪은 불편함은 범인이 걸렸는지 확인하려면 직접 기기에 가서 파일 생성 여부를 체크해야 했다는 점이다. 터치스크린을 연결해두기는 했지만 매일 기기 체크하기가 귀찮아서 범인이 잡히면 이메일을 보내는 스크립트를 만들었는데, 이는 생각보다 단순하다. (코드) 구글, 네이버 등의 이메일 SMTP 서버를 사용해서 내 계정에서 내 이메일로 메일을 보내도록 설정하면 된다. 단점으로는 이메일 자체가 느리고, 내 핸드폰의 이메일 동기화 주기가 그렇게 짧지 않아서 즉각적인 메일 확인이 어렵다는 점이다. 물론 커피 머신 범인을 확인하는게 급한 문제는 아니었기에 만족하면서 사용했다.

 

그러나 다른 컴퓨터에서 코드를 돌리고 실행이 완료되거나, 문제가 발생하면 알려주는 기능은 커피 머신보다는 급한일이다. 더욱이 이메일로 보내는 경우 다른 중요치 않은 이메일에 섞여서 묻힐 수 있다. 때문에 기존 메신저 앱들 중 편리한 API 구성을 제공하는 것이 없나 검색하다가 Telegram의 bot을 활용하는 방법이 군더더기 없이 제일 깔끔해서 이를 소개하려고 한다.

 

Why Telegram?

 사실 텔레그램하면 지난 몇 년간 언론을 달군 사건이 생각나서 설치 자체가 조금 꺼려지기는 했으나 군더더기 없이 깔끔하다는 말에 이를 택했다. 카카오톡이 추가앱 설치도 필요없어서 가장 유력한 후보였으나, 플러스친구, 로그인 등 각종 기능 등과 같이 묶여있어서 그런지 너무 복잡해 반쯤 만들다가 포기했다. 또한 정확히 "코드 다 돌아가면 알려줘!"의 목적에 부합하는 bot 기능을 가지고 있기에 Telegram을 택했다.

 

How to

먼저 핸드폰에 Telegram 앱을 설치하고 계정을 만든다. 연락처 동기화를 자꾸 요구하는데 그닥 메인 메신저로 쓸 생각이 없어서 차단했다.

1. 먼저 BotFather를 사용자에서 찾아서 새로운 bot을 만든다.

계정명을 꼭 확인하자

2. BotFather 와 대화를 시작하고 /newbot 을 보내면 이름과 id를 물어보는 작업을 거쳐 bot을 만들어준다.

이름은 그렇다쳐도 id는 중복이 있으면 안되는 점이 조금 귀찮다.

해당 token은 이미 비활성화 했으므로 괜찮다.

해당 과정을 완료하면 HTTP access token을 발급해준다. 이 정보가 있어야 해당 봇을 통해 본인의 핸드폰으로 메시지를 보낼 수 있다. 함께 알려주는 api 사이트에 들어가면 생각보다 많은 기능을 지원한다는 것을 확인가능하다. 하지만 우리는 단 세 가지 함수만 사용할 예정임으로 굳이 들어가볼 필요는 없다.

 

3. HTTP 호출을 통해서 bot 생성여부를 체크한다.

HTTP GET, POST 등의 method 테스트를 위해서 예전에 Postman이라는 프로그램을 배웠고, 이런 프로젝트마다 디버깅 목적으로 사용하는데 간단한 튜토리얼임으로 기본 웹브라우저를 사용해서 설명하려한다. 별개로 Postman 프로그램이 꽤 유용하니 시간되면 꼭 살펴보시길.

https://www.postman.com/

 

Postman API Platform | Sign Up for Free

Postman is an API platform for building and using APIs. Postman simplifies each step of the API lifecycle and streamlines collaboration so you can create better APIs—faster.

www.postman.com

웹 브라우저(본인은 파이어폭스) 주소창에 아래와 같이 입력을 하면 생성한 bot과 연결이 잘 되는지 확인할 수 있다.

https://api.telegram.org/bot아까발급받은토큰/getMe
# 예시 : https://api.telegram.org/bot5413916344:AAE88PzAed9FCOxygDEeSsEQaggKd8-F81o/getMe

문제가 없다면 아래와 같은 ok 사인을 받을 수 있다.

이름과 id를 얻을 수 있다

4. bot에게 말을 걸자.

핸드폰으로 돌아가서 bot 생성시 두번째로 입력했던 id로 사용자를 검색하면 해당 bot을 찾을 수 있다. 이 bot과 대화를 시작하고, 메시지 하나를 보내두자. bot은 자체적으로 사용자에게 최초로 메시지를 보낼 수 없다. (스팸방지)

 

5. 생성된 대화방의 id를 불러온다.

https://api.telegram.org/bot아까발급받은토큰/getUpdates
# 예시 : https://api.telegram.org/bot5413916344:AAE88PzAed9FCOxygDEeSsEQaggKd8-F81o/getUpdates

다시 브라우저로 돌아가서 위 주소로 간다. 성공적으로 호출이 되면 아래와 같이 대화내용을 불러올 수 있을 것이다.

 

 

getMe를 호출하기 이전에 먼저 대화부터 보내고 나중에 getUpdates를 호출했더니 앞선 대화가 누락되는 문제를 확인했다. (첨부한 이미지에서도 보낸 메시지는 hi there부터 시작하는데  불러온 대화내용은 그뒤에 보냈던 "ㅁㅁ"만 보인다.) 다시 메시지를 보내면 getUpdates에서 잘 보이는 것 같으니 큰 문제는 아닌 것 같다.

 

이렇게 성공적으로 대화를 받으면, id 값을 확인한다. 이 경우 55201이다.

 

6. http request를 통해 메시지를 보내자

모든 과정이 끝났다. 메시지를 보내는 함수는 sendMessage이며 아래와 같이 사용하면 된다.

https://api.telegram.org/bot아까발급받은토큰/sendMessage?chat_id=아이디&text=보낼메시지
# 예시 : https://api.telegram.org/bot5413916344:AAE88PzAed9FCOxygDEeSsEQaggKd8-F81o/sendMessage?chat_id=55201&text=Code Finished

 

python의 경우는 request 패키지를 설치한 후, 아래의 함수를 넣어주면 된다.

import requests
requests.get('https://api.telegram.org/bot5413916344:AAE88PzAed9FCOxygDEeSsEQaggKd8-F81o/sendMessage?chat_id=55201&text=Code Finished')

text 뒤에 코드가 돌아갔다는 정보 외에 시간, 에러가 발생한 경우 그 에러 내용등의 첨부가 가능하다.

 

무엇보다 모든 과정이 핸드폰으로 진행이 가능하며, access token, 본인 telegram 대화방의 id, 단 두 개 정보만 확인되면 메시지를 바로 보낼 수 있다.

 

주의

github가 금광산이라는 말을 들은 적이 있다. 특히 요 얼마전에는 아마존 AWS에 스타트업에서 사용하는 기업용 계정 access token을 실수로 github에 그대로 올렸다가 수 십억에 달하는 사용료가 부과되었다는 뉴스가 나온적이 있다. 모르는 사람은 바보 같다고 생각하겠지만 평상시 버전 관리 프로그램을 쓰는 습관 때문에 나도 충분히 할법한 실수라고 생각한다. Telegram bot token으로는 그러한 짓을 할 수 없겠지만 access token이 어딘가에 공개가 되지 않도록 꼭 주의하자. 본 예시에 사용된 계정은 전부 비활성화 처리를 완료했다.

Posted by Knowblesse
취미/Technology | 2022. 2. 23. 11:11 | /42
 

Notion 입문 1일차

#스타트업 #힙스터 #팀워크 #생산성 스타트업'스러움'이 하나의 문화컨텐츠로 떠오르면서 온갖 제품과 서비스들이 이러한 분위기에 맞추어지고 있다. 마치 Helvetica 폰트가 디자이너들에게 사랑

blog.knowblesse.com

이전에 Notion을 하루 써보고 소감을 간략히 적어봤었는데 이제 사용을 시작한지 한달이 되었다. 지금쯤이면 평이 달라지지 않았을까.

 

장점

1. Markup

이 기능이 생각보다 편리하다. [ / ]버튼 하나로 각종 형식들을 만들 수 있고 evernote에 비하면 적은 노력을 들이고도 읽기 편한 문서를 만들 수 있다는 점이 가장 큰 강점으로 작용한다. 사실 낙서를 포함하는 자유도가 높은 메모는 실제 종이에서 하는 것이 가장 좋다. 디지털화가 필요한 문서의 경우 어느정도 구조화된 아이디어인 경우가 많은데 evernote는 이를 표현하기에는 '동기화가 가능한 메모장'에 지나지 않았다. Notion은 이 '구조화된 아이디어'를 정리하기에 정말 적합한 서비스이다.

구조화된 아이디어 정리에 적합한 Notion

2. Page 구조

이것은 Notion의 장점이라기 보다는 OneNote가 구현해내지 못한 단점에 가까울까. OneNote도 hierarchical 구조를 가지고 있기는 하다만 제한적인 depth를 가지고 있고 최상위 객체인 전자 필기장이 로딩이 느리다는 인상을 자주 받는다. 그에 비해서 Notion은 무한히 Page를 확장할 수 있고, 가볍다는 것이 확실히 느껴진다.

 

Notion과 OneNote의 구조

단점

1. Spell check

아니 Spell check가 제대로 안된다는게 있을 수 있는 일인가. 어째서인지 내 환경에서는 spell check 기능이 작동하지 않고 turn on/off 메뉴도 나타나지 않는다. 그리고 공식 설명에 따르면 setting page에 따로 spell check 기능 관련 메뉴가 없다던데 이 부분은 정말 아쉽다. 아쉬운대로 Grammarly 의 윈도우 add-on을 사용하고 있는데 이 부분은 시급히 개선해야할 것 같다. 

 

2. Keyboard Shortcut

한컴오피스에 익숙한 한국인이라 그런지 키보드 단축키의 부족함을 느낀다. 다른 앱들도 별반 다를 것이 없겠다만 이러한 기능도 구현이 되었으면 하는 아쉬움이 남는다.

 

 

총평

사용 1일차에 느꼈던 감상과 다르지 않다.

'이렇게 참신하고 대단한게 나오다니!' 보다는 '이런게 왜 이제야?'에 가깝다.

사실 노트 작성 프로그램이 '참신하다'라는 평을 받을 가능성이 거의 없음을 감안하면 최고의 평가가 아니었나 싶다. Vim 정도 되면 악랄함을 포함한 참신성을 느낄 수 있겠다만 글쓰고 동기화 시켜주는 에디터에 더 바랄게 뭐가 있으랴. 전반적으로 크게 불편함을 느끼는 부분이 존재하지 않고, 깔끔하게 완성되는 output이 흡족함을 준다. 무엇보다 notion을 쓰면서 evernote가 괘씸해졌다. 초반의 무료정책에 반하게 점점 가격이 올라가고 기능은 변하지 않는 모습에 결국 Notion 사용 한달차에 올 8월까지 결제가 되어있는 유료 플랜을 해지했다. Notion도 이제 시장에 진입한 초기 서비스라 나중에 결제 플랜이 어떻게 바뀔지는 모르겠으나 지금 수준의 가격만 유지된다면 사용료 때문이라도 이쪽으로 넘어올 가치가 충분히 있는 것 같다. 

Posted by Knowblesse

Intro

협업하는 팀의 사정으로 원래 쓰던 tensorflow를 놔두고 pytorch로 갈아타게 되었다. 때문에 포스트의 제목이 이런 식이다.

사실 tf v1 때는 변수 만드는 방법부터 배워야 할 정도로 사용성이 아주 부족했는데 keras의 등장 및 v2에서의 편입 이후로 레토르트 식품 데우듯이 쉽게 ML 모델 개발이 가능해졌다. 이러나 저러나 복잡한 모델을 만들지 않는 이상tensorflow와 pytorch 모두 주인장에게는 cuda를 편하게 쓰게 해주는 도구에 지나지 않고 이쯤 시간이 흘렀으면 사용성이 엇비슷하지 않을까 하는 생각에 1시간 정도 걸리리라 생각했다.

 

당장 튜토리얼부터 마음에 안들었다.

 

pytorch tutorials

동영상 자료는 재끼고, Basic에는 대뜸 Tensor 설명부터, Example에는 간단한 Iris, MNIST는 있지도 않다. 그나마 있는 example은 Image Classification. numpy랑 연동이 잘 된다고 하기엔 받아먹는 data형 제한이 너무 귀찮았고 (Linear function은 float32가 아니면 안받더라) 구조도 와닿지 않는다. keras에 익숙한 사람은 누구나 pytorch로 넘어가며 고구마 100개 순간을 경험하리라 생각해 간단한 Iris classification tutorial을 여기에 올려둔다. 구조적 특성이나 보는 관점은 전부 tensorflow에 익숙한 사람의 의견이라 처음부터 pytorch를 쓴 경우 '이 사람은 왜 이런 소리를 하나?' 생각을 할 수 있겠으나 주인장과 같은 전처를 밟은 사람이라면 이해하리라 기대한다.

 

Example

import torch
from torch import nn as nn
import torch.nn.functional as F
from sklearn import datasets
import numpy as np

# Define Model
class Clf(torch.nn.Module):
    def __init__(self):
        super(Clf, self).__init__()
        self.fc1 = nn.Linear(4,10)
        self.fc2 = nn.Linear(10,3)

    def forward(self,x):
        return self.fc2(F.relu(self.fc1(x)))

# Load Data
iris = datasets.load_iris()

X = torch.tensor(iris.data, dtype=torch.float32)
y = torch.tensor(iris.target)

# Setup Model, Optimizer, Loss function
clf = Clf()
optimizer = torch.optim.Adam(clf.parameters(), lr = 0.01)
loss_fn = nn.CrossEntropyLoss()

# Train
for _ in range(1000):
    y_pred = clf(X)
    loss = loss_fn(y_pred, y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

print(np.argmax(y_pred.detach().numpy(),1))

dataset은 scikit-learn에서 들고오는 방식을 택했다. train test split, metric 등은 모두 무시하고 150개 데이터 전체를 한번에 학습시키는 단순무식한 코드임을 감안해서 보면 좋겠다.

 

Model

tensorflow 의 keras와 동일하게 모델을 정의하고 모델 구조를 짜서 넣어준다. 하나 차이점은 one-line으로 모델을 만들지는 못하고, nn.Module을 상속받는 class를 만들고, forward 함수를 구현해서 모델을 짜야한다는 점이다. forward 함수를 한줄로 짠 것을 보면 주인장의 귀찮음을 볼 수 있다. 모델은 별 문제없이 넘어갈 수 있다.

 

Data Loading

data를 넣을 때부터 슬슬 짜증이 나기 시작한다. scikit-learn에서 제공하는 iris 데이터는 numpy.ndarray 형태이다. X데이터는 float64, y데이터는 int64의 형태를 가지고 있다. 문제는 모델에서 사용한 nn.Linear가 망할 float32만 받는다는 것이다.

 

# Load Data
iris = datasets.load_iris()

X = torch.tensor(iris.data, dtype=torch.float32)
y = torch.tensor(iris.target)

때문에 tensor로 바꿀 때 dtype 을 명시하거나 처음부터 np.astype 함수를 사용해서 float32로 바꾸어서 들고와야한다.

 

Model setup

loss function을 정하고 optimizer를 정하는 것은 tensorflow와 유사하다. 원하는 것을 가져다가 쓰면 된다.

한가지 유의점은 keras의 경우 model 안에 loss function과 optimizer가 같이 포함되는데 pytorch에서는 이들이 다 따로 논다.

clf = Clf()
optimizer = torch.optim.Adam(clf.parameters(), lr = 0.01)
loss_fn = nn.CrossEntropyLoss()

 

Training

# Train
for _ in range(1000):
    y_pred = clf(X)
    loss = loss_fn(y_pred, y)
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()

tutorial의 training 파트의 코드를 볼 때는 문제가 없었는데 막상 짜다보니 멘붕이 왔다.

 

잠깐, 나 optimizer에 아무것도 안 넣었는데..?

 

더보기

clf(X) 호출을 통해서 y_pred를 구한다. foward 함수가 아마 호출이 될 것이다.

loss_fn도 이해가 된다. y_pred와 y_true(=y)를 넣어서 loss를 구한다.

optimizer.zero_grad()는 아마도 무언가 initialize를 하는 부분일 것이고,

loss.backward()는 함수명으로 보아 error를 통한 미분값을 구할 것 같은데(backward propagation) weight을 어떻게 알아서 미분값을 구하지?

심지어 loss 값은 optimizer에게 전달되지도 않았는데 혼자서 step() 함수를 호출하고 있다.

 

keras에서는 하나의 모델 안에 loss 함수와 optimizer가 같이 존재한다.

model = keras.Sequential(
    [
        keras.Input(shape=(4,)),
        layers.Dense(200, activation="relu", kernel_initializer=keras.initializers.random_normal(mean=1)),
        layers.Dense(200, activation="relu", kernel_initializer=keras.initializers.random_normal(mean=1)),
        layers.Dense(2,name='output')
    ]
)

model.compile(
    optimizer=keras.optimizers.SGD(learning_rate = 0.001),
    loss=keras.losses.MeanSquaredError(),
    metrics=[keras.metrics.MeanSquaredError()]
    )

심지어 metric 함수까지 포함되어 compile 과정을 거치면 model.fit()을 호출하는 것으로 loss와 optimizer가 작동하고 metric을 알려주는 기능까지 겸하고 있다. keras는 model만 들고다니면 그 안에 모든 함수와 weight들이 묶여서 돌아다니는 구조인데 pytorch는 쉽게 납득하기 어려운 형태를 취하고 있다.

 

pytorch 내부를 뜯어보고 document를 뒤적이고, pytorch를 많이 쓰는 아는 동생에게 물어본 결과 겨우 작동 방식을 이해했다.

왜 이렇게 만든 것인지는 전혀 와닿지는 않지만 말이다.

 

먼저 gradient descent를 위해서는 세 step이 필요하다. 1) error, 혹은 loss를 계산하고, 2)이를 통해 gradient 값을 구한 뒤, 3) 일정한 learning rate을 유지하든 momentum을 더하든 실제로 weight을 수정하는 것이다.

 

keras의 경우 loss function은 loss를 계산하는 부분(1)만 담당하고, gradient를 구하고, 적절한 learnint rate에 맞게 수정하는 부분(2,3)은 optimizer가 담당하고 있다.

 

pytorch는 다르다. loss function에서 1과 2를 담당하고, optimizer가 gradient만 사용해서 실제 parameter를 수정한다.(3)

 

코드 실행 순서를 거꾸로 올라가면 다음과 같다.

1. optimizer가 주어진 gradient 값을 사용해서 parameter를 수정한다.

optimizer.step()

optimizer가 처음 만들어졌을 때 clf.parameters()를 넣었는데, 이는 모델의 parameter를 참조하는 값이다. 때문에 optimizer는 clf 의 parameter를 수정할 수 있다. 수정에 사용하는 gradient 값은 parameter 안에 들어있다.

params = [i for i in clf.parameters()]
print(type(params[0]))
print(params[0].grad)

위 코드를 실행하면 torch.nn.parameter.Parameter 형태로 clf 내부가 표현되는 것을 알 수 있으며, 이는 Tensor의 일종으로, backward 함수를 실행하면 grad 값이 생긴다. loss.backward()를 실행하지 않으면 params[0].grad는 None으로 설정되어 있을 것이다.

 

2. loss 값을 사용해서 gradient를 구한다.

loss.backward()

그렇다면 어떻게 loss를 통해 구한 gradient가 clf의 weight까지 가는가?

loss는 Tensor type이다. 중요한 점은 이 Tensor가 단순히 data만을 담고 있지 않고, 어느 과정을 통해서 이 값들이 구해졌는지에 대한 정보를 가지고 있다는 것이다. 설마설마 하긴 했는데 이 변수 안에 어떻게 보면 이 값이 나오게 된 역사(?)가 담겨있다. (이러면 memory issue는 괜찮나...?)

tutorial에서도 제대로 알려주지 않는데 이를 확인하는 방법은 다음과 같다.

(Deep Learning with PyTorch: A 60 Minute Blitz 글 안에는 있다!)

loss.grad_fn.next_functions

Tensor object인 loss에는 grad_fn이라는 필드가 있는데, 이 function 안의 next_functions field에는 이 뒤에 호출해야할 function들의 참조가 달려있다. 물론 이 순서는 해당 Tensor가 만들어진 순서의 역순이다. 이렇게 next_functions 를 따라가면 아래의 순서로 loss가 구해졌음을 알 수 있다.

 

grad_fn 호출 stack

때문에  loss.backward()만 호출하면 각 parameter Tensor에 grad 값이 전파된다.

각 grad_fn에 대한 자세한 설명은 공식 document에도 나와있지 않지만, AccumulateGrad가 있는 부분마다 실제 error에 의한 grad 값들이 존재하는 것으로 보인다 (각 linear layer마다 weight에 해당하는 Tensor와, bias에 해당하는 Tensor가 있을 것이다.)

3. clf를 구성하는 Tensor들의 grad 값을 초기화 한다.

optimizer.zero_grad()

optimizer와 loss function이 따로 놀기 때문에 현재 parameter가 가지고 있는 gradient를 초기화 시켜주는 함수가 존재한다. 때문에 이 함수를 먼저 호출하고, 이 뒤에 loss를 backpropagation 시키는 방식으로 코드를 작성해야한다.

 

이 이후는 keras를 쓰던 유저도 쉽게 이해하리라 생각한다.

 

마치며

pytorch를 사용한 ML 논문이 이미 tensorflow를 사용한 논문의 수를 넘었다고 한다. 익숙하지 않은 구조 때문에 하루를 날리며 대체 왜 pytorch를 쓰는가에 대한 글을 찾다가 pytorch의 장점으로 동적인 computation graph를 언급하는 글을 보았다. 어쩌면 keras처럼 Model 종속적인 형태가 아니라 연산 값에 연산과정이 들어있는 구조 때문에 동적 computation graph가 가능한 것인가 하는 생각이 든다. 하나의 작은 산은 넘은 것 같다.

 

 

Posted by Knowblesse
취미/Technology | 2022. 1. 11. 11:58 | /40

#스타트업 #힙스터 #팀워크 #생산성

스타트업'스러움'이 하나의 문화컨텐츠로 떠오르면서 온갖 제품과 서비스들이 이러한 분위기에 맞추어지고 있다. 마치 Helvetica 폰트가 디자이너들에게 사랑받기 시작하면서 세계를 장악하는 현상을 보는 것만 같다. 실제 생산성을 높이기 위함이든, 본인의 '힙함'을 보여주기 위함이든 여러 제품을 구입하고, 서비스를 구독하기 시작하면 월 구독료로 나가는 돈은 적지 않을 것이다. 또한 개중에는 이러한 문화적 흐름에 편승해서 그럴듯하게 포장한 예쁜 쓰레기를 제공해주는 곳도 많아서 사용자로 하여금 더 많은 판단을 요구한다. 자칫 생각없이 모든 것을 수용하다가는 생산성 앱으로 도배된 생산적이지 않은 본인의 기기를 보게 될 것이다.

 

변화에 슬슬 더디게 반응하기 시작하는듯한 기분을 떨쳐내기 위해 오래전부터 이야기를 듣고 추천을 받아온 Notion을 쓰기 시작했다. 당장에 연구실 지식베이스와 소통을 위한 목적으로 어렵게 도입했던 Slack부터 스타트업에 잠시 참여했을 때 접했던 트렐로, 2011년 1월부터 사용중인 에버노트와 iOS 한글 지원문제로 갈아탔던 OneNote까지 온갖 유사 프로그램들 접했는데 각자 지향하는 바가 분명하게 다른 것 같다. 다른 앱과 함께 하루 Notion을 써본 소감을 간략히 적는다.

 

* 트렐로 : 협업용 TODO 리스트

* Keep : 동기화 되는 포스트잇 게시판

* Evernote : 동기화 되는 간단한 메모장

* OneNote : 화이트보드

* Slack : 조금은 DB의 형태를 갖춘 카카오톡

* Todoist / TickTick : 혼자쓰는 TODO 리스트

Notion : 잘 정리된 Blog 글 같은 Evernote

현재 각자 사용목적성이 뚜렷하고 열심히 쓰고 있는 두 앱이 OneNote와 Evernote여서 그런지 자꾸 이 둘과 비교를 하게 된다. Evernote의 경우 기사를 대충 스크랩해서 처박아두거나, 짧은 메모, 일기 등의 줄글 위주의 게시물, 혼자 보는 용도의 설명서를 담아둔다. '동기화가 되는 간단한 메모장'이 가장 확실한 설명인 것 같다. 노트를 예쁘고 깔끔하게 만들기 위해서는 추가적으로 서식을 변경하거나 하는 등의 노력이 많이 들어간다. 그런데 그럴 일은 없다. 대충 넣어두고 나중에 필요하면 찾으면 되고, 잘 정리된 문서가 필요하면 워드로 작성하면 된다. 첫 번째 책상서랍 같은 존재다. 때문인지 유료플랜을 결제할 때는 매번 망설인다. 해주는 기능도 얼마 없으면서 이 가격을 받아야 하나 싶다.

 

OneNote의 경우는 아이디어 상자이다. 화면 아무 곳이나 클릭하면 바로 글이 써지고 Indent 조절이 쉽게 되고 이리저리 컨텐츠 박스를 끌고 다닐 수 있다는 점이 매력이다. 화이트보드 같은 매력이 있다. 하지만 이러한 점은 화면이 작은 모바일 기기로 볼 때 확실히 단점으로 작용한다. 화면안을 이리저리 돌아다니면서 글을 읽는 것은 정말 불편하다. 그래도 Evernote에 비해서는 깔끔한 문서정리가 가능하기 때문에 자주 꺼내보아야 하는 자료의 경우는 OneNote에 정리해서 넣어둔다.

 

짧은 메모, 텍스트 위주의 정보, '대충 넣어두어도 나중에 필요하면 뒤지겠지...' 하는 수준의 정보는 Evernote.
빠른 아이디어 작성, 위계관계가 확실한 정보, 조금은 정리해서 넣어두어야 하는 정보는 OneNote.

 

 

Notion은 OneNote와 Evernote 어딘가에 어중간하게 있다. 많은 사람들이 마크업 기능을 장점 중 하나로 꼽는데 확실히 여타 다른 앱들에 비해서 월등히 편리하다. 그 외에 sorting이 지원되는 깔끔한 표 삽입 기능과 댓글기능, 페이지 간 링크 등은 앞서 언급한 많은 앱들의 필수 기능들을 하나로 통합해서 왜 이런 앱이 이제야 나왔는지 싶다. 하지만 '이렇게 참신하고 대단한게 나오다니!' 보다는 '이런게 왜 이제야?'에 가깝다. 다양한 템플릿을 지원한다는 점에서는 OneNote와 가깝고 심플하다는 것에 Evernote와 비슷한 느낌을 준다. 하지만 전반적인 감상은 OneNote보다는 Evernote에 가깝다고 할까. OneNote의 '화이트보드'스러움이 있었다면 Evernote와 OneNote를 통합해서 사용할 수 있었겠으나 아이디어를 이리저리 끌고다니는 기능은 없는듯 하다. 하긴 앞서 언급한 모바일환경에서의 불편함을 생각하면 좋지 않은 방법일 수도 있겠다. Notion에서는 확실히 '협업용'의 분위기가 물씬 풍긴다. 내 난잡한 Evernote 글을 동료에게 준다면 한소리 들을 것이 분명하고, 마크업이 제대로 되지 않고 외부자료를 넣기 힘든 OneNote는 장기적인 데이터 유지 관점에서는 분명 좋지 않은 선택이다. Notion은 원활한 정보전달에 필요한 만큼의 미적 요소를 가미해서 글 작성이 가능하다. 왜 이렇게 붐을 일으켰는지 알법하다.

 

하지만 Notion이 얼마나 내가 쓰는 기존 프로그램들을 대체할 수 있을지는 의문이 든다. '협업'의 필요성이 크지 않기에 기존 두 프로그램들로도 충분한 목적을 달성했다. 또한 마크업 기능도 미를 위해 할애하는 시간을 해치지 않을 만큼 쉽게 되어있기는 하지만 미가 필요할 수준의 내용이면 아에 문서로 작성을 하고, 그런 것이 아니라면 짤막한 메모로도 충분하다. 그나마 이번에 진행하는 프로젝트의 일원이 Notion을 쓰는 것 같던데 이 프로젝트를 위해서 사용하는 것이라면 Notion을 충분히 쓰겠으나 OneNote나 Evernote를 내려놓기에는 조금 힘들 것 같다. 다만 메모장 프로그램에 매달 돈을 지불해야하는 점이 괘씸하기에 Evernote는 Notion로 대체를 시도하려고 한다.

Posted by Knowblesse