Archive

Author Archive

프로 Git 읽는 법

April 27th, 2013 No comments

Git은 서브버전보다 사용하기 어렵다. 아마 대중적인 버전관리도구 중 가장 어려울 것이다. 서브버전은 따로 공부하지 않고 썼던 개발자라도, Git을 쓰려면 공부를 해야한다.

Git을 공부하는 최고의 방법은 책 ‘프로 Git‘을 읽는 것이다. 프로 Git을 처음 읽는 순간 그것을 확신했으며, 이후 사용자로서 Git을 익히고, 부분적으로 Git을 구현해보기도 하고, 사내에 Git 교육과정을 만들어 진행해본 이후에도 그 생각은 변하지 않았다.

그러나 아무리 훌륭한 책이라고 할지라도, 오직 버전관리도구만을 위해 200페이지가 넘는 책을 처음부터 끝까지 모두 읽어야 한다는 것은 당장 개발을 시작하고 싶어하는 개발자들에게는 너무나도 지루하고 고통스러운 일이다.

다행히도 이 책을 반드시 모두가 완독해야만 하는 것은 아니다. 필요에 따라서 골라 읽어도 무방하다. 예를 들어 당장 내일부터 Git을 써서 개발을 시작해야 하는 상황이라면, 이 책의 1.3.5절과 2장을 읽고 연습해서 Git의 사용법을 익히면 된다.

어떤 챕터를 언제 읽는 것이 좋을지 적어보면 다음과 같다.

  1. 시작하기 – 가급적이면 읽자. 정 읽기 싫으면 “1.3.5 세 가지 상태”만이라도 읽자. 그것만은 절대로 읽어야 한다. 읽지 않으면 Git을 쓸 수 없다.
  2. Git의 기초 – 이것을 읽으면 Git을 쓸 수 있게 된다. 반드시 읽자
  3. Git 브랜치 – 이것을 읽으면 Git을 잘 쓸 수 있게 된다. 왠만하면 읽자.
  4. Git 서버 – Git 서버를 직접 운영한다면 읽어야 한다. 그렇지 않다면 읽지 않아도 무방하다. 물론 읽으면 서버를 이해할 수 있게 되어서 좋기는 하다.
  5. 분산 환경에서의 Git – 여러명이 협업한다면 읽는 것이 좋다.
  6. Git 도구 – 뭐 더 좋은 기능 없어? 라는 생각이 들 때 읽어도 무방하다.
  7. Git 맞춤 – 위와 마찬가지로, 이런 설정은 없나? 라는 생각이 들 때 발췌해서 읽어도 무방하다.
  8. Git과 다른 VCS – Subversion과의 차이가 궁금하다면 읽자.
  9. Git의 내부구조 – Git을 능숙하고 편안하게 다루려면 반드시 그 내부를 이해해야 한다. 팀에 한 명 정도는 이 챕터의 내용을 이해할 수 있는 수준에 달해있는 것이 좋다.

이런 훌륭한 책을 지은 스캇 샤콘과, 한국어로 번역한 박창우, 이성환, 최용재님께 깊은 감사를 드리고 싶다.

Categories: Git, Tags:

400 Bad Request와 403 Forbidden의 의미에 대해

April 23rd, 2013 No comments

HTTP/1.1

HTTP/1.1을 정의한 최신 명세인 RFC 2616에 따르면, 흔히 알고 있는 것과는 달리 의외로 403 Forbidden은 권한(authorization)에 대한 에러가 아니다. 그냥 요청은 이해하지만 수행을 거절(refuse)하겠다는 의미이다.

그리고 또 의외로 400 Bad Request는 그냥 “요청이 잘못되었다”라는 의미가 아니라 “요청의 syntax가 잘못되어서 이해를 못하겠다”라는 의미를 담은 응답이다.

400 Bad Request에 대한 설명1

The request could not be understood by the server due to malformed syntax.

403 Forbidden에 대한 설명2

The server understood the request, but is refusing to fulfill it.

HTTP/1.1 개정판

그런데 HTTP/1.1 개정판에선 흔히 알려진대로가 거의 맞다. 403 Forbidden은 권한(authorization)에 대한 에러가 맞고, 400 Bad Request는 그냥 요청에 문제가 있어서 처리를 못하겠다(혹은 안하겠다)는 에러다.

400 Bad Request에 대한 설명3

The server cannot or will not process the request, due to a client error (e.g., malformed syntax).

403 Forbidden에 대한 설명4

The server understood the request, but refuses to authorize it.

요새 점점 RFC 2616보다 개정판을 따르는게 낫겠다는 생각이 들고 있다. 별 차이 없겠지 싶어서 그냥 RFC 2616을 따르고 있었는데, 의외로 이렇게 차이가 나는 부분이 있다. 어차피 개정판이라고 해봐야 설명만 고친거고 프로토콜의 요구사항 자체는 둘 다 같으니 동작에 문제를 일으키는 일은 없겠지.

  1. http://tools.ietf.org/html/rfc2616#section-10.4.1 []
  2. http://tools.ietf.org/html/rfc2616#section-10.4.4 []
  3. http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-22#section-6.5.1 []
  4. http://tools.ietf.org/html/draft-ietf-httpbis-p2-semantics-22#section-6.5.3 []
Categories: HTTP Tags:

절대경로가 “//”로 시작하는 URL은 스펙 위반일까?

March 1st, 2013 2 comments

http://example.org//foo/bar 와 같이 path의 절대경로가 //로 시작하는 것은, URI의 정의에 대한 최신 명세인 RFC 3986을 위반한 것이다. 절대 경로는 “//”로 시작해서는 안된다.

  path-absolute   ; begins with "/" but not "//"

그러나 HTML에서 <a href="//foo/bar">test</a> 와 같은 상대경로를 사용하는 것은 아무 문제가 없는데, 왜냐하면 HTML의 가장 최신 표준인 4.01(아직 5는 드래프트)은 RFC 3986보다 훨씬 오래된 RFC 1808에 따라 상대경로를 해석하기 때문이다. RFC 1808의 “2.4.3. Parsing the Network Location/Login”에 보면 url이 //로 시작하는 경우, // 이후부터 다음번 / 이전까지의 문자열을 net_loc(network location과 login information)로 삼게 되어있으므로, //foo/barhttp://foo/bar로 해석된다.

또한 HTTP/1.1 역시 URI를 해석할 때 RFC 3986이 아닌 그보다 오래된 RFC 2396을 따르므로 앞의 URL은 HTTP/1.1 명세를 위반한 것도 아니다.

그러나 HTTP/1.1은 곧 개정될 예정이며, 이 개정될 HTTP/1.1은 RFC 3986을 따르므로 결국 명세를 위반하는 것이 되지 않을까 걱정이 살짝 되는데, 다행히도 HTTP Working Group에서 몇 주 전에 이 문제를 발견하고서는 논의를 거친 끝에 절대경로가 //로 시작해도 되는 것으로 예외처리를 했기 때문에 위반이 아니게 될 것이다. 다만 그렇다고 해도 어디까지나 이건 HTTP에 국한된 이야기이므로, HTTP 응용프로그램이 아닌 소프트웨어를 개발할 때는 //로 시작하는 절대경로를 사용하면 RFC 3986을 위반하게 된다는 점을 신경써야 할 것이다.

그리고 마지막으로, 로이 필딩에 따르면 RFC 3986이 절대경로가 //로 시작할 수 없도록 막은 것은, URI 규칙을 새로운 ABNF로 재작성하는 과정에서 발생한 사고(accident)라고 한다.

> Roy, do you recall whether there's a reason why we would
> want to rule out a path starting with "//"?

No, it is an accident of the transition to new URI ABNF and
should be raised as an issue.  There are several different ways to
fix it, depending on how lenient we want to be with parsing.

위 메일의 전문은 여기서 확인할 수 있다.

Categories: HTTP Tags:

HTTP/1.1의 Range 요청과 이를 활용한 Pagination

February 10th, 2013 4 comments

HTTP/1.1은 Range 요청을 지원한다. 클라이언트가 Range 헤더를 통해 어떤 리소스의 일부분만을 요청하면, 서버는 그 부분만을 반환하는 방식으로 동작한다.

/file.zip를 100바이트에서 200바이트까지만 가져오는 클라이언트의 요청과 그에 대한 서버의 응답은 다음과 같다.

요청

GET /file.zip HTTP/1.1
Range: bytes=100-200
...

응답

HTTP/1.1 206 Partial Content
Content-Range: bytes 100-200/500
...

위의 예에서 단위는 ‘bytes’를 사용했다. 사실 HTTP/1.1을 정의한 RFC 2616에서는, HTTP/1.1에서 정의한 단위는 bytes 뿐이며 HTTP/1.1 구현체는 bytes 이외의 다른 단위는 무시할 수 있다(MAY)고 되어있다. 이해할 수 없는 단위에 대해서 서버와 클라이언트가 어떻게 동작해야 하는지에 대해 명확히 정의가 되어있지 않아서 직접 단위를 정의해서 쓰기가 매우 겁이 난다. 무슨 일이 벌어질 지 확신할 수 없기 때문이다.

다행히 httpbis(곧 제출될 HTTP/1.1의 개정판)에서는 훨씬 명확하게 정의되어있다.

If a range unit is not understood in a request, a server MUST ignore
the whole Range header field (Section 5.4).  If a range unit is not
understood in a response, an intermediary SHOULD pass the response to
the client; a client MUST fail.

만약 서버가 요청의 range 단위를 이해할 수 없다면, 반드시(MUST) 모든 Range
헤더 필드를 무시해야 한다. 만약 응답의 range 단위를 이해할 수 없다면, 중개자1
는 응답을 클라이언트로 넘겨줘야 하며, 클라이언트는 반드시(MUST) 실패해야 한다.

Range 요청을 이용한 pagination 구현

이러한 HTTP의 미래에 조금 용기를 얻어서, 나는 Range 요청을 이용하여 웹 애플리케이션의 pagination2 을 구현했다. 직접 정의한 “pages”라는 단위와 HTTP/1.1의 Accept-Ranges, Range, Content-Range 헤더를 이용한다. 각 헤더의 사용방법은 다음과 같다.

Accept-Ranges

어떤 리소스가 pagination을 지원한다면, 서버는 그 사실을 알리기 위해 해당 리소스에 대한 일반적인 GET 요청에 대한 응답에 “pages” 값을 갖는 Accept-Ranges 헤더를 포함시킨다.

예:

Accept-Ranges: pages

Range

클라이언트는 다음과 같은 형식의 Range 헤더를 이용해 이 리소스의 특정 페이지만을 요청할 수 있다.

Range             = pages-unit "=" page-number
pages-unit        = "pages"
page-number       = 1*DIGIT
DIGIT             = <any US-ASCII digit "0".."9">

예:

Range: pages=1

Content-Range

서버는 Range 요청에 대해 다음과 같은 형식의 Content-Range 헤더를 통해 206 Partial Content로 응답한다. (HTTP/1.1의 bytes-range-spec과 차이가 있음에 유의하라)

Content-Range     = pages-unit SP page-number "/" complete-length
pages-unit        = "pages"
page-number       = 1*DIGIT
complete-length   = 1*DIGIT
SP                = <US-ASCII SP, space (32)>

예를 들어 총 두 페이지로 이루어진 목록에서 첫번째 페이지만을 반환하는 응답에서의 Content-Range는 다음과 같다.

Content-Range: pages 1/2

서버는 상황에 따라 클라이언트가 요청한 것과 다른 페이지를 돌려줄 수도 있다. 이러한 상황에 대한 예외처리의 책임은 클라이언트에게 있다.

이 구현에 대한 전체 문서는 여기를 보라.

왜 굳이 Range 요청을 이용하는가?

왜 Pagination을 구현할 때 굳이 Range 요청을 이용해야 하는가? 단지 동작하게 만든다는 이유 뿐이라면 query string에 page=1 같은 key-value를 넣기만 해도 충분할 것이다.

그러나 내가 원하는 것은 표준화된 인터페이스와 동작이다. HTTP를 이해하는 개발자라면, “Range 요청을 활용해서 pagination을 구현했다”는 한마디로도 대강의 동작방식을 이해할 수 있을 것이며, 예외상황에 대한 처리도 명세를 따르면 되기 때문에 매번 개발자들끼리 토론하고 협의할 필요도 줄어들 것이다. 혹시 HTTP를 모르는 개발자라고 할 지라도, 특정 애플리케이션만의 pagination 방식을 익혀서 사용하는 것 보다는, 널리 쓰이고 있으며 앞으로도 널리 쓰일 표준화된 Range 요청에 대해 배워두는 편이 경력에 도움이 될 것이다.

  1. 캐시 같은 것들 []
  2. 목록 하단에 표시되는 Prev 1 2 3 4 5 Next 이런 것. []
Categories: HTTP Tags:

파일시스템별 timestamp 정밀도

February 3rd, 2013 2 comments

대부분의 파일시스템은 파일들마다 생성시각, 최종변경시각, 최종접근시각을 항상 기록해 둔다.

그런데 이 시간들의 정밀도는 파일시스템별로 제각각이다. 위키백과를 뒤져서 정리해보았다.

  • FAT는
    • 최종변경시각은 2초
    • 생성시각은 10밀리초
    • 최종접근시각은 1일
    • 삭제시각은 2초
  • HFS+는 1초
  • ext2,3도 1초
  • exFAT는 10밀리초
  • NTFS는 100나노초
  • ext4는 1나노초

ext4의 등장 이후 1나노초 수준의 정밀도를 요구하는 목소리가 많아지면서, 각 프로그래밍 언어 등에서도 이에 대한 지원이 들어가기 시작했다.

정정
  • java8이 1나노초 정밀도를 지원하게 되는 줄 알았는데, r6404를 보니 오버플로우 방지를 위해 1마이크로초로 잘라내고 있었다. (2013/02/03)
Categories: 프로그래밍 Tags: ,

근황

November 10th, 2012 No comments

사실 여긴 근황같은 건 안 적는 블로그인데, 하도 글을 안 쓴지가 오래되서 마치 죽은 블로그처럼 보일까봐 하나 쓴다.

연말에 한해를 돌이켜 볼 때 “대체 이 땐 뭘 한거지?” 하고 궁금해할 미래의 나를 위한 것이기도 하다.

  • 10월엔 1주일에 두개씩 글을 쓸 계획이었는데 그 절반수준밖에 쓰지 못했다. cousera 강의를 두개나 듣기 시작했기 때문이다. 컴파일러하고 스칼라.
  • 11월에라도 좀 쓰려고 했는데 10월보다도 더 바빠져서 불가능하다. cousrea 강의도 계속 들어야 하는데 예상치 못했던 일거리들까지 생겨났기 때문이다. (회사 서비스 장애, 센터내 스칼라 발표, SKP 테크플래닛에 대타로 출장)
  • 지금 하는 프로젝트가 11월이 마감이라 한달 내내 계속 바쁠 예정이다.
  • 따라서 블로그는 12월에 재개할 것이다.
Categories: 일상 Tags:

Git의 Staging Area는 어떤 점이 유용한가

October 23rd, 2012 7 comments

Git에는 Staging Area라는 공간이 있다. 어떤 변경사항이 저장소에 커밋되기 전에, 반드시 거쳐야만 하는 중간단계이다.

다른 버전관리도구에는 이에 정확히 대응하는 것은 없다. 저장소가 추적하는(관심의 대상이 되는) 파일들의 목록을 유지하고, 그 파일들에 대한 메타데이터를 관리하는 것은 다른 저장소들도 하는 일이지만, Git 처럼 커밋될 예정인 파일의 내용들까지 기억하지는 않는다.

이 Staging Area의 존재는 처음 Git을 사용하는 입장에서는 그저 불편만 안겨주고 이해만 더디게 만들어주는 목적불명의 무언가에 지나지 않는다. 다른 SCM에선 파일을 고치고 바로 커밋하면 되었는데, Git은 반드시 그 전에 add를 해 줘야 한다. 이런 비용을 감수하면서까지 Git이 Staging Area라는 공간을 사용자들에게 노출시키고 있는 이유는 무엇일까. 어떤 상황에서 Staging Area가 유용한지 살펴보면서 이해해보자.

일부분만 커밋할 때

Working directory에서 코드를 고칠 때는 자유롭게 고치고 싶다. 하지만 커밋할때는 atomic하게 하고 싶다. 이를 가능하게 하려면 커밋할 때 working directory 전체가 아니라 부분만 커밋하면 될 것이다.

staging area라는 것이 없다고 가정해보자. ‘커밋될 예정인 내용들’을 디스크에 저장해 둘 공간이 없으므로, 커밋할 때 어느 부분을 커밋할지 한번에 결정해야 한다. 한번에 어렵지 않게 결정할 수도 있지만 시간을 들여 생각해야 할 수도 있다. 시간을 들여 조금씩 커밋할 부분을 결정해 나간다고 하면, 그 시간동엔 나의 결정사항들은 메모리상에 존재해야 한다. 메모리에 올라와있는 정보는 디스크에 저장되어 있는 정보보다 다루기 까다롭다. Staging Area에 저장되어있는 ‘곧 커밋될 예정인 내용’들은 쉽게 git diff --staged등의 명령으로 확인할 수 있겠지만, 이것이 메모리에 올라와있는 상태라면, 해당 메모리 영역을 사용하고 있는 프로세스를 찾아서 물어봐야 알 수 있다. 실수로 그 프로세스를 죽였다면 공들여 커밋을 준비하던 것이 허사가 된다.1

충돌을 해결할 때

충돌을 해결해야 하는 상황에서도 비슷한 문제가 있다. 5개의 파일을 머지했는데 그 중 2개에서 충돌이 발생했다고 해 보자. 3개의 파일은 그냥 커밋하면 되고 2개는 충돌을 해소(resolve)해줘야한다. 여기서도 위와 같은 문제가 생긴다. 큰 규모의 충돌이 발생한 경우 이를 해결하는 것은 상당히 시간이 들어가는 일이고, 이를 위한 작업 데이터를 메모리에만 올려놓는 것은 참으로 불안하다. 따라서 이 정보 역시 Staging Area와 같이 디스크의 어떤 공간에 저장해두는 것이 훨씬 좋을 것이다. 이것은 Staging Area가 없는 Subversion이라도 마찬가지다. 서브버전은 어떤 파일이 충돌했는지 ‘파일 상태’를 기억한다. 그러나 이보다는 Git의 방식이 더 낫다. 서브버전은 파일 단위로 충돌 여부를 기억하기 때문에, 파일의 일부분만 충돌을 해소한 후 커밋하는 것은 매우 어렵기 때문이다. Git이라면 간단하다. 충돌이 발생한 파일에서, 필요한 만큼만 충돌을 해소하고 그 부분만 add하여 커밋하면 된다.

커밋 다시하기

commit --amend 명령으로 커밋을 다시 할 때도 Staging Area의 존재는 빛을 발한다. 커밋을 다시 할 때 로그메시지만 고치는 것이 아니라 파일들도 좀 고치고 싶다면, commit --amend 전에 파일을 고쳐서 Staging Area에 add하기만 하면 된다. Staging Area가 없었다면 commit --amend시에 Git에게 다른 방법으로 패치를 전달해주었어야 했을 것이다.

요약

Staging Area는 단순하지만 유용한 저장공간이다. 많은 Git의 명령들이 Staging Area를 활용하여 Git을 더욱 생산적인 도구로 만들어준다.

물론 Staging Area라는 생소한 개념이 Git의 진입장벽을 더욱 높이는 것은 사실이지만, 그럼에도 불구하고 나를 비롯한 Git을 즐겨쓰는 많은 개발자들에게 Staging Area는 여전히 필수불가결한 존재이다. 누군가 이해하기 쉬우면서도 현 Staging Area에 대한 요구를 흡수할(혹은 불필요하게 만들) 수 있는 어떤 개념을 만들어낸다면 몰라도 그 전까지는 Git의 핵심 기능이자 특징으로서의 지위가 변하는 일은 없을 것이다.

관련글

며칠전에 Git 메일링리스트에 Staging Area가 왜 필요한지에 대해 질문을 했었는데, 많은 분들이 회신을 주셨다. 그 회신들을 통해 알게 된, Staging Area의 이점에 대해 설명하는 두 개의 글을 소개한다.

  1. 커밋을 깔끔하게 만드는데 과도하게 공을 들인다는 생각이 드는가? 하지만 이런 개발자들이 실제로 있다. []
Categories: Git Tags:

Scala의 variant

October 14th, 2012 No comments

Scala의 variant는 낯설고도 헷갈리기 쉬운 개념이다.

개념 정의

A <: B 일때 C[A] <: C[B] 라면, C는 covariant다. A <: B 일때 C[A] >: C[B] 라면, C는 contravariant다.

역도 성립한다.

C가 covariant라면, A <: B 일때 C[A] <: C[B]이다. C가 contravariant라면, A <: B 일때 C[A] >: C[B]이다.

선언하는 법

C를 covariant로 선언하려면,

class C[+A] { ... }

C를 contravariant로 선언하려면,

class C[-A] { ... }

아래 코드의 두번째줄에서 타입 에러가 발생하지 않으려면,

object Nil extends List[Nothing] { ... }
val x : List[String] = Nil

List는 아래와 같이 covariant로 선언되어야 한다.

class List[+T] { ... }

이렇게 하면 List가 covariant이므로, String >: Nothing 일때 List[String] >: List[Nothing] 이다. 따라서 에러가 발생하지 않는다.

Categories: 프로그래밍 Tags:

Scala와 Haskell의 문법

October 8th, 2012 2 comments

Functional Progrmaming Principles in Scala 수업을 듣고 스터디를 하면서 배운 스칼라의 문법 및 언어 특징들을 Haskell과 비교하면서 적어본다.

Type inference

Scala는 함수를 정의할 때 return type이 무엇인지 생략해도 알아서 추론을 할 수 있다.

scala> def double (x: Int) = x * 2
scala> :type double
(x: Int)Int

하지만 formal parameter의 타입은 생략할 수 없다.

scala> def double (x) = x * 2
<console>:1: error: ':' expected but ')' found.
   def double(x) = x * 2
               ^

Haskell은 둘 다 생략해도 정확하게 추론해준다.

Prelude> let double x = x * 2
Prelude> :t double
double :: Num a => a -> a 

currying

Scala는 currying을 쉽게 할 수 있도록, multiple parameter list를 지원한다.

scala> def sum1 (x: Int, y: Int) = x + y
scala> def sum2 (x: Int)(y: Int) = x + y
scala> sum1(3, 4) == sum2(3)(4)
res2: Boolean = true 

Haskell도 지원한다.

Prelude> let sum1 (x, y) = x + y
Prelude> let sum2 x y = x + y
Prelude> sum1(3, 4) == sum2 3 4
True

Haskell은 어째 후자가 더 자연스러워보인다.

High order function

Scala에서 function은 first class citizen이다.

scala> def apply (f: Int => Int, x: Int) = f(x)
scala> def double (x: Int) = > x * 2
scala> apply(double, 4)
res1: Int = 8

apply의 첫번째 parameter에 들어갈 함수가 Int가 아닌 다른 타입을 다룰 수 있게 하려면 다음과 같이 타입 파라메터를 명시해야한다.

scala> def apply[T, U](f: T => U, x: T) = f(x)
scala> def double (x: Int) => x * 2
scala> apply(double, 4)
res2: Int = 8
scala> def hello (x: String) => "hello, " + x
scala> apply(hello, "world")
res3: java.lang.String = hello, world  

Haskell은 타입 추론이 잘 되니 타입 파라메터가 필요없다.

Prelude> let apply (f, x) = f x
Prelude> let double x = x * 2
Prelude> apply(double, 4)
8
Prelude> let hello x = "hello, " ++ x
Prelude> apply(hello, "world")
"hello, world"

Anonymous function

Scala에서 익명 함수(anonymous function)를 정의하는 문법은 보통 함수을 정의하는 문법과 살짝 다르다.

scala> def sum (x: Int, y: Int) = x + y
scala> sum(3, 4)
res1: Int = 7
scala> ((x: Int, y: Int) => x + y)(3, 4)
res2: Int = 7

솔직히 항상 헷갈린다. 특히 ==>로 바뀌는 부분이.

Haskell도 마찬가지로 익명 함수 정의 문법이 별도로 있다.

Prelude> let sum x y = x + y
Prelude> sum 3 4
7
Prelude> (\x y -> x + y) 3 4
7

Haskell도 =->로 바뀐다. 뿐만 아니라 파라메터 목록 앞에 \가 더해진다.

Scala나 Haskell이 맨날 쓰는 언어면 익명함수 문법 정도 그냥 외워버리겠지만 어쩌다 한번 쓰는거다보니 매번 까먹고 적어놓은 것을 뒤적거리게 된다.

Categories: 프로그래밍 Tags: ,

우분투에서 와이파이가 안될때

October 7th, 2012 No comments

나는 맥북에어에 우분투를 사용한다. MacOSX를 쓰다가 수 개월 전에 넘어왔다.

우분투가 MacOSX보다 못한 점 중 하나가 와이파이가 잘 안된다는 점이다. 단순히 늦게 연결되는 정도가 아니라 아예 불통인 경우가 수두룩하다.

우분투에서 인터넷이 안될 때 시도해 볼 수 있는 몇가지 팁을 적어본다.

애플릿 재시작

무선 인터넷이 안되서 와이파이 애플릿에서 AP를 바꿔보려고 하는데 전혀 먹통인 경우가 있다. 아무리 클릭해도 아무런 반응을 하지 않는다.

이럴 땐 그냥 애플릿을 재시작하면 된다. 우분투에서 사용하는 와이파이 애플릿 이름은 nm-applet이다. killall로 죽이고 다시 띄우자.

드라이버 바꿔보기

애플릿에는 아무 문제가 없는데 아무리해도 AP에 연결이 안되는 경우가 있다. 이럴 땐 드라이버를 바꿔보자.

맥북에어 + 우분투에서 쓸 수 있는 드라이버는 wl(broadcom closed driver)과 brcmsmac(opensource driver)의 두 가지가 있는데 wl로는 잘되고 brcmsmac으로는 안 될 때가 있는가 하면 그 반대의 경우도 있다.

wl을 쓰려면 Applications > Additional Drivers에서 Broadcom STA wireless driver를 켜면 된다. 혹시 wl 드라이버를 블랙리스트에 추가한 상태라면 지운다.

$ sudo rm /etc/modprobe.d/blacklist-wl.conf

brcmsmac을 쓰려면 wl을 블랙리스트에 넣고, /etc/modules 파일에 brcmsmac을 추가한다.

$ echo 'blacklist wl' |sudo tee -a /etc/modprobe.d/blacklist-wl.conf
$ echo 'brcmsmac' |sudo tee -a /etc/modules

둘 다 안된다면 업데이트된 버전의 wl을 사용해보자. 여기에 업데이트하는 방법이 나와있다.

그래도 안되면

주위에 쓰는 사람이 많아서 안되는 경우도 있다. 똑같이 쓰는 사람이 많아도 MacOSX에선 잘되고 우분투에서는 안될 수 있다. 사람이 줄어들기를 기다리자.

Categories: Uncategorized Tags: