메일주소와 비밀번호만으로 SMTP 설정방법 알아내기

이메일 발송을 위해 메일 에이전트에 SMTP를 설정하려면 알야아 하는 것이 많다. 대략 다음과 같다.

  • 호스트네임
  • 포트번호
  • 사용자이름
  • 비밀번호
  • SSL/TLS 사용여부

하지만 일반 메일 사용자들이 알고 있는 것은 자신의 메일주소와 비밀번호뿐이다. 이것만 가지고 간단히 메일 발송 설정을 하도록 할 수는 없을까?

어느정도 가능하다. you@gmail.com이라는 메일주소에서 SMTP 설정을 알아내는 예를 들어보겠다.

메일주소의 구성

RFC 5322 Section-3.4.1에 따르면 메일주소는 다음과 같이 local-port와 domain으로 구성된다.

addr-spec       =   local-part "@" domain

따라서 you@gmail.com 이란 메일주소에서는 you가 local-port이고 mail.com이 domain이 된다.

설정 알아내기

대개 사용자 이름은 메일주소이거나 local-port이다. 둘 다 상관없이 사용자이름으로 정상 인식하는 경우도 있다. 여기선 메일주소를 사용자 이름으로 간주하겠다.

RFC 6186은 DNS의 SRV 레코드를 이용해 이메일 서버에 대한 정보를 얻어내는 방법을 제시한다. (( SMTP 뿐 아니라 IMAP과 POP3도 가능하다. )) 이 예에서와 같이, 메일주소에서 domain을 얻어낼 수 있으므로, dnslookup을 이용해 SRV 레코드를 읽어 SMTP 서버의 호스트네임과 포트번호를 알아낼 수 있다.

다음은 gmail.com 도메인에 Email Submission에 대한 질의를 보내 응답을 얻은 예이다.

$ nslookup -type=srv _submission._tcp.gmail.com
server:     127.0.0.1
Address:    127.0.0.1#53

Non-authoritative answer:
_submission._tcp.gmail.com  service = 5 0 587 smtp.gmail.com.

위의 결과에서, SMTP 서버의 호스트네임은 smtp.gmail.com이고, 포트번호는 587임을 알 수 있다. 포트번호가 587이라는 것에서 TLS를 사용할 것이라고 추측할 수 있다.

따라서 you@gmail.com의 SMTP 설정은 다음과 같다.

  1. 호스트네임: smtp.gmail.com (dnslookup으로 알아냄)
  2. 포트번호: 587 (dnslookup으로 알아냄)
  3. 사용자이름: you@gmail.com (대개 메일주소와 같다)
  4. SSL/TLS 사용여부: TLS 사용 (포트번호 587에서 추측함)

이 방법의 한계 / 혹은 좀 더 알아봐야 할 것

  • DNS의 SRV 레코드를 통해 SMTP 서버에 대한 정보를 얻어낼 수 있는 경우가 흔치 않다. 사실 gmail.com외엔 모두 실패했다.
  • 사실 포트번호만으로는 TLS를 사용하는지의 여부를 결정하기 어렵다. 이 문제의 해결을 위해 TLSA 레코드를 이용하는 방법에 대한 제안이 있다. fanf-dane-mua-00 드래프트를 보라.
  • 사용자이름은 그냥 메일주소인 경우가 많지만 항상 그런지는 확신할 수 없다.
광고

SMTP 설정에서 SSL과 TLS의 차이

메일 클라이언트를 설정할 때, 메일 발송을 위해서 POP3 혹은 SMTP 설정을 하게 된다. SMTP 설정의 경우, 보안 설정을 SSL로 하는 경우도 있고 TLS로 하는 경우도 있다.

SSL과 TLS는 무엇이 다른가? SSL이냐 TLS냐를 잘못 선택하면 메일 발송 기능이 동작하지 않는 것으로 보아서 둘이 서로 다른 것 같지만 사실 둘은 한 줄기다. SSL 3.0이 버전이 올라가면서 TLS 1.0이 된 것이다. 다시 말해 SSL은 TLS의 옛날 이름이라고 봐도 크게 틀리지 않다. (( 이 둘의 차이는 파이어버드와 파이어폭스의 차이 만큼밖에 없다, 라고 하려다가 파이어버드의 인지도는 파이어폭스의 백분의 일도 안되는 것 같아 그만두었다. ))

SMTP로 메일을 발송할 때 SSL이냐 TLS냐에 따라 달라지는 것은 딱 한가지다. SSL로 통신하는 경우에는 처음부터 암호화된 통신을 하게 되나, TLS의 경우엔 처음엔 암호화되지 않은 hello 메시지를 전달하는 것에서 시작한다. 이 메시지를 주고 응답을 받아 상호간 암호화된 통신이 가능함을 확인한 뒤에서야 비로소 암호화된 통신을 시작한다. 이 시점부터는 SSL이나 TLS나 같다. 자세한 설명은 이 글을 보라.

대부분의 메일 서비스라면 두 가지 다 지원할 것이다. 그런데 메일 에이전트나 메일 발송 라이브러리가 TLS를 제대로 지원하지 않는 경우가 있다. 혹시 TLS로 해서 안된다면 SSL로 바꿔보자. 포트번호도 물론 바꿔줘야 한다. TLS를 사용하는 경우는 보통 587이고 SSL이라면 465일 것이다.

iOS6 사파리의 POST 캐싱 버그에 대해

iOS6의 사파리는, 기본적으로 POST 요청에 대한 응답을 캐싱한다.

여기서 치명적인 문제가 발생한다. 서버에서 요청의 대상이 되는 리소스가 갱신되었음에도 불구하고, 사파리는 캐시했던 이전 응답을 사용자에게 보여주게 될 수 있다.

문제가 발생하는지에 대한 테스트는 여기에서 해볼 수 있다. 이 테스트에서 브라우저는 두 번의 POST 요청을 서버에게 보내고 서버는 그 요청에 대해 각각 다른 응답을 한다. 그러나 iOS6의 사파리에서는 두 번 전부 같은 응답을 받게 될 것이다.

회피책

이 문제를 회피하는 두 가지 방법이 있다.

  • 클라이언트 사이드: 캐시되지 않도록 query string에 매번 바뀌는 임의의 문자열(예: 현재 시각)을 붙인다.
  • 서버 사이드: POST 요청에 대한 응답에, Pragma: no-cache 나 Cache-control: no-cache 를 명시해준다.

위 둘 중 하나만 적용해도 문제가 발생하지 않는다. 자세한 설명은 Guillermo Rauch의 ‘iOS6 AJAX 버그 이해하기‘를 보라.

누구의 잘못인가?

애플의 잘못이다. HTTP/1.1을 정의한 RFC 2616의 POST에 대한 설명을 읽어보면, POST 요청에 대한 응답은 기본적으로 캐시해서는 안됨을 알 수 있다. 응답에 Cache-Control 이나 Expires 헤더가 정의되어있는 경우에만 캐시를 할 것인지에 대해 고려할 수 있다.

자세한 것은 HTTPbis Working Group의 의장인 Mark Nottingham이 이번 건에 대해 포스팅한 ‘POST 캐싱‘을 읽어보라.

vim에 능숙해지는 법

vim은 익숙해지기 어려운 에디터로 정평이 나 있다. 단축키를 외우지 않고서는 글자 하나도 입력할 수 없기 때문이다.

내가 vim을 배운 방법

나는 2007년 9월부터 vim을 쓰기 시작해서, 2008년 봄 쯤 부터는 능숙하게 사용할 수 있게 되었다. 내가 vim을 익힌 방법은, 그냥 배운 것을 적는 것 뿐이다.

  1. 단축키나 기능을 익혔다면 적어둔다.
  2. vim을 쓰다가 모르는 기능이 있다면 우선 적어둔 것에서 찾아본다. 없다면 찾아보고 적는다.
  3. 완전히 외운 것은 지운다.

완전히 외워서 지우게 된 명령어의 갯수가 15개를 넘어서면 별 불편없이 vim을 사용할 수 있을 것이다(몇달 걸린다).

부연설명

  • 외운 것을 지우는 이유는, 다음번에 찾는 시간을 줄이기 위함이다. 적은 것이 10-20개라면 그 중에서 하나 찾는데 별로 시간이 들지 않지만 30-40개쯤 되면 찾기도 피곤해진다.
  • 지우는 것에 대해 너무 망설이지 말자. 혹시 외웠다고 생각했는데 까먹었다면 그냥 다시 구글링해서 적어넣으면 된다. 그래도 걱정이 된다면 언제라도 지운 것을 복원할 수 있도록 이 메모를 git 등으로 버전관리하는 방법을 추천한다.
  • 적을 필요 없이 매번 구글링하면 되지 않나 하는 생각이 들 수도 있는데, 구글링을 통해 찾은 글을 해석해서 적용 가능한 해결책을 찾아내는 것 보다는 직접 적은 것을 보는 것이 시간면에서 효율적이다. 또한 나는 익힌 것을 적는 행위와, 나중에 그것을 보면서 적었을 때의 상황을 연상하는 행위가 학습효과를 높일 것이라고 믿고 있다.

이 방법에 대해 더 자세하게 잘 설명한 글이 있어 링크한다: vi 배우기 — “컨닝 페이퍼” 기법

vim에 컴파일러/인터프리터의 에러메시지 연동시키기

대부분의 IDE에서는, 컴파일 에러나 런타임 에러가 발생했을 때, 소스코드에서 해당 에러의 원인이 된 라인으로 바로 이동하는 기능이 있다.

물론 vim에서도 약간의 설정으로 가능한 기능이다. 설정 및 사용법을 알아보자.

makeprg 설정

우선 현재의 소스코드를 해석하거나 컴파일할 때 사용할 프로그램을 설정한다. 여기서는 python으로 해 보겠다.

:set makeprg=python

이렇게 하고 나면, :make %로 현재 편집중인 파일을 파이썬으로 실행해 볼 수 있다.

errorformat 설정

다음은 vim이 파이썬의 에러메시지를 해석할 수 있도록 에러포맷을 정의해야 한다.

:set errorformat=%C %.%#,%A  File "%f"\, line %l%.%#,%Z%[%^ ]%\@=%m

이제 :make %로 편집중인 소스코드를 파이썬으로 실행했을 때 에러가 발생한다면, 자동으로 커서가 에러가 발생한 위치로 옮겨진다.

파이썬이 출력한 에러메시지에서 어떤 줄이 위의 식에 매칭된다면, vim은 그 줄이 소스코드에서 에러의 원인이 되는 위치를 알려주는 것으로 생각하고 해석하여 파일명, 라인번호 등의 정보를 얻어낸다. 위의 식이라면 %f에 대응하는 것이 파일명, %l에 대응하는 것이 라인번호다.

예를 들어 파이썬이 아래와 같은 메시지를 출력한다면,

Traceback (most recent call last):
File "test.py", line 13, in <module>
    x = MyClass()
TypeError: __init__() takes exactly 2 arguments (1 given)

vim은 두번째 줄을 인식해서 test.py와 줄번호 13을 얻어낼 것이다.

자세한 문법은 :help errorformat으로 확인해보자.

만약 errorformat과 대응하는 에러메시지가 1개 이상이라면, cncp로 각 에러를 오갈 수 있으며, clist로 에러메시지의 목록을 확인할 수 있다.

단축키 한번에 실행하기

매번 :make %을 입력하는 것이 번거롭다면, 다음과 같이 단축키 한번에 실행되도록 할 수도 있다. 다음은 f5 키로 실행하도록 하는 설정이다.

:map<f5> :make %<cr>

설정파일에 설정하기

물론 위에서 언급한 errorformat이나 makeprg의 설정 등은 모두 언어별로 다를 수 밖에 없다. 따라서 vim의 syntax 설정 디렉토리에 넣어두는 것이 좋을 것이다. 파이썬이라면 ~/.vim/syntax/python.vim 파일에 다음의 설정을 추가하면 된다.

set errorformat=%C %.%#,%A  File "%f"\, line %l%.%#,%Z%[%^ ]%\@=%m
set makeprg=python
map<f5> :make %<cr>

vim과 쉘을 연동하여 사용하기

vim은 그 자체로도 상당히 강력하지만, 쉘과 함께 사용함으로써 서로의 강력함을 곱할 수 있다. vim과 쉘을 연동해서 쓰는 방법들을 몇가지 소개한다.

여기서 소개하는 방법들은 gvim이 아닌 터미널에서 실행하는 vim을 위한 것이다.

쉘로 나갔다가 돌아오기

vim 사용중에 Ctrl+z로 쉘로 나가서 명령을 실행한 뒤, 쉘에서 fg를 입력해 vim으로 돌아온다. 이건 vim과는 아무 상관도 없는 기능이지만, 사실 vim에서 쉘 명령을 써야 할 때 가장 많이 쓰는 방법이다. (쉬우니까)

screen 혹은 tmux

vim은 자신 안에서 쉘을 띄우는 기능이 없다. 플러그인이 있긴 한데 그다지 쓸만하지 않다. 필요하다면 그냥 screen이나 tmux를 써서 창을 쪼개 한쪽 창에 vim을 띄우는 것이 훨씬 낫다.

옛날엔 screen에서 세로 쪼개기를 지원하지 않아 영 쓰기기 좋지 않았지만, 버전 4.01이후부터는 잘 지원한다.

screen에서는 C-a S로 창을 쪼개고, C-a <Tab>으로 창을 오가며, C-a c로 쪼개진 창에 쉘을 띄운다.

tmux에서는 C-b "로 창을 쪼개고, C-b o로 창을 오간다. 창이 쪼개지는 순간 바로 해당 창에 쉘이 실행되므로 별도로 쉘을 실행해 줄 필요는 없다.

쉘 명령 실행

쉘 명령은 :!로 실행할 수 있다. 예를 들어, ls를 실행해본다면,

:!ls

:r!로 표준출력을 바로 붙여넣을 수 있다. ls를 실행한 결과를 vim에 바로 붙여넣고 싶다면,

:r!ls

선택한 영역을 쉘명령의 표준입력으로 넣어 표준출력으로 출력된 결과가 그 영역을 대체하게 할 수도 있다. 선택한 영역에서 특정 단어가 포함된 줄만 남기려면,

:'<,'>!grep word

클립보드

엄밀히 말해 클립보드는 쉘과 아무련 관련이 없지만, 많은 사용자들이 GUI 환경에서 가상터미널을 띄워 쉘을 사용하고 있을 것이고, 그렇다면 클립보드를 통해 vim에 복사-붙여넣기를 해야하는 상황도 꽤 자주 발생할 것이다.

클립보드에 복사하려면 복사할 영역을 선택하고,

"+y

클립보드에 들어있는 문자열을 vim에 붙여넣기하려면,

"+gP

클립보드와 연동하여 복사/붙여넣기를 하려면, vim이 xterm_clipboard가 가능한 상태로 빌드되어 있어야 한다. vim --version을 했을 떄, +xterm_clipboard가 보인다면 가능한 상태인 것이다.

vim-slime

shell 연동의 종결자라 할 수 있는 굉장한 플러그인이다. 뭐가 가능한고 하니, screen이나 tmux로 창을 쪼개서 한쪽 창에는 vim을 띄우고 다른 창에는 repl을 띄운 다음에, vim에서 작성한 코드를 바로 다른 창의 repl에 보내서 실행시킬 수 있다.

단점은 잘 동작하지 않는다는 것이다.

vim-slime은 screen과 tmux를 지원하는데, 여기서는 screen과 함께 사용하는 법을 소개하겠다.

우선 .vimrc 설정에 몇가지 추가해야 하는 것이 있다.

$ echo 'let g:slime_target = "screen" >> ~/.vimrc

screen은 표준입력을 받아들이지 못하므로, 대신 파일을 이용해 통신해야 한다. 이 때 사용할 파일도 설정해준다.

$ echo 'let g:slime_paste_screen = "~/$HOME/.slime_paste"' >> ~/.vimrc

screen을 실행한 뒤 창을 하나 쪼개고 세션의 이름을 적당히 붙여준다. 여기서는 ‘shell’이라고 이름붙였다.

$ screen -S shell

그리고 다른 창에 가서 vim을 열고 간단히 코드를 작성한 뒤,

Ctrl+c Ctrl+c

이렇게 하면 현재 줄이 옆 창의 쉘에 입력된다. 다만 처음에는 세션 이름과 윈도 이름을 물어본다. 세션 이름은 위에서 정했고, 윈도 이름은 아마 0일 것이다.

한번 입력하고 나면 다시 묻지 않는다. 잘못 입력했다면 다음의 명령으로 다시 입력할 수 있다.

Ctrl+c Ctrl+v

git의 revision에 대해

git에서 revision이란 특정 Git object를 가리킬 수 있는 표현식을 의미한다. HEAD~2, master, a8dd808db6d87a1d809b1a223e08ab69602b2d3a, HEAD:test.txt 등이 모두 “revision”이다.

문법

ABNF 로 표현해보면 대략 다음과 같다.

DIGIT = "0" / "1" / "2" / "3" / "4" / "5" / "6" / "7" / "8" / "9"
sha1 = 1*40HEXDIGIT
refname = name / "refs" "/" (name /
          ("tags" "/" name) /
          ("heads" "/" name) /
          ("remotes" "/" name) /
          ("remotes" "/" name "/" HEAD))
specifier = "@" "{" (date / num) "}"
rev = sha1 / refname [specifier] / specifier /
      rev ("^" / "~" / "^" DIGIT / "~" DIGIT)
revision = rev /  ":" "/" regexp / ":" ("0" / "1" / "2" / "3") ":" path

그닥 잘 알려져 있지는 않지만 간혹 유용한 몇가지 표현을 소개해 보겠다.

@{…}

@을 이용해, 특정 refname(브랜치 등)이 과거에 가리켰던 커밋을 지칭할 수 있다. 쉽게 말해서 git reflog에 나열되는 특정 커밋을 지칭할 수 있다.

  • <refname>@{n} – refname이 과거에 가리켰던 커밋. git reflog 해보면 무슨 의미인지 알 수 있을 것이다. master@{5} 와 같은 표현이 가능하다.
  • <refname>@{date} – 특정 날짜, 시각에서 refname이 가리켰던 커밋. master@{yesterday} 식의 표현이 가능하다.

:n:path

:n:path 표현식으로, 주어진 path에 대해 특정 상태에서의 tree 혹은 blob을 지칭할 수 있다.

  • :0:path – staging area

머지중인 상태에서는 공통 조상이나 특정 브랜치의 path에 대응하는 tree나 blob을 지칭할 수 있다.

  • :1:path – 공통 조상(common ancestor)
  • :2:path – 현재 브랜치
  • :3:path – 현재 브랜치에 머지되어지는 중인 브랜치

더 자세히

man gitrevisions로 더 자세한 설명을 볼 수 있다.