컴언

브런치 작가가 되다

분류없음

브런치 작가가 됐어요! 

출시 초창기에는 "미디엄(Medium) 배낀 거다", "이것도 곧 망할 서비스다"(당시 다음과 카카오가 합쳐지면서 여러가지 서비스가 밥먹듯이 종료됐었거든요 ㅠㅠ) 말이 많았지만, 

지금 와서 보면 분명 미디엄 같은 플랫폼과 차별점도 있고, 카카오에서도 은근히 신경쓰는 것으로 보입니다. 지금 제 블로그가 있는 티스토리 망하기 전까지는 안전하겠죠?

특히 출판 지원을 눈여겨볼만 합니다. 단 한 권이라도 출판이 가능하다고 하니 기대되네요. 그래서 열심히 글 쓰려고요. 

일요일 밤에 열일해주셔서 감사합니다...

브런치 작가 심사

브런치에서는 누구나 글을 쓴 다음 "작가의 서랍"에 담아둘 수 있지만 다른 사람들이 볼 수 있게 글을 "발행"하려면 작가 신청을 해서 심사를 받아야합니다. 

이 심사기준이 정확히 뭔지는 공개가 되어있지 않은데요, 저는 처음에 실제로 책을 출판해본 작가나 유명한 사람들(예: 박원순 서울시장)만 작가가 될 수 있는지 알았습니다. 

그런데 출판 경험은 없지만, 글 잘 쓰는 지인들이 브런치 작가에서 흥미로운 글들을 연재하는 걸 보며 저도 평소에 해보고 싶던 글쓰기를 브런치에서 하고 싶어서 작가 신청을 하게 됐습니다.


먼저 저는 컴퓨터 과학과 언어학을 대중들이 쉽게 이해할 수 있도록 풀어쓰는 매거진을 쓰고 싶다고 하면서 제 뚜렷한 관심사를 어필했습니다. 

매거진 프롤로그, 목차, 매거진에 들어갈 글 두편을 샘플로 제출했고요, 이 블로그와 제 개인 웹사이트도 신청서에 포함한게 끝입니다. 

저같이 허접한 필력의 사람도 통과가 된 것 보면, 글쓰기 실력보다는 글을 쓰는 진정성과 꾸준히 연재할 작가인가를 보는 듯 합니다. 


저는 작가 신청을 하고 4일 뒤, 일요일 밤 9:15에 작가가 되었다는 메일을 받았는데요, 주말에도 일하시는 것 같아서 놀랐습니다. 더불어 일요일 밤에 이 행복을 안겨주신 데에 무한한 감사를...



제 브런치는 https://brunch.co.kr/@lqju 고요, 여기에 있는 "수리 나형보다 쉬운 컴퓨터 과학" 매거진의 프롤로그와 1,2 편을 참고하셔서 이런 글도 통과가 되는구나... 아하... ㅇ_ㅇ 하시면서 브런치 작가 신청에 도움이 됐으면 좋겠습니다. 

미국에서 다래끼 없앤 썰

분류없음

미국에서 다래끼가 나다

저는 밤만 새면 꼭 다래끼(sty)가 납니다. 특히 미국에서는 평소에 숙제를 최대한으로 미뤘다가 밤새서 하는 안좋은 습관때문에, 밤을 샌 다음 날은 어김없이 다래끼에 시달려야했거든요.

하루면 가라앉지만 도저히 가라앉지 않는 녀석이 있으면 병원을 갔었죠. 미국에서도 아주 징한 놈이 걸린 적이 있었는데요, 아시다시피 미국은 외국인/유학생이 병원 갔다가는 돈 엄청 깨지고 오기 쉬워요. 이를테면 다래끼 짜는데 몇백 불 들고, 당일 치료같은 거 받을래도 기본 200불 깨지죠. 다행히 저는 학교 보험에서 무료로 진료해주는 학교 clinic 이 있어서 그 곳에 간 적이 있었습니다.


미국의 진료는 우리나라의 진료와 매우 다릅니다. 우리는 의사 선생님 방에 들어가면 바로 본론으로 들어가죠? 저희 학교 clinic 은 갈 때마다 매번 평소 건강 상태 등을 묻는 서론이 엄청 길었던 기억이 납니다. 다래끼로 갔는데 간이 시력검사도 했어요... ㅋㅋㅋ 한 환자당 진료 시간을 30분으로 잡더군요. 그래서인지 당일 예약도 엄청 힘듭니다 ㅠㅠ


미국에서 처방받은 "눈찜질"

(c) Wikivisual via WikiHow

본론에 들어와서...의사샘이 플래시로 눈을 이리저리 확인하시네요.

"Warm compression 해 본 적 있어요?" 라고 저한테 물어봅니다. "그게 뭔가요?" 

제 무지함에 행복한 얼굴로 밖에서 수건을 하나 가져오십니다. 알고보니 눈찜질 같은 것이더군요. 

수건을 보여주시며 집에가면 깨끗한 수건에 따뜻한 물을 적셔서 다래끼 난 눈에 20-30분 정도 하루에 3번 찜질을 하라고 말씀해주셨습니다.



설마 이 양반... 안아키??

한국에서는 매번 안약을 처방해줬고, 이 안약이면 정말 시원하게 다래끼가 날라가는 경험을 한 저는 의사 샘의 처방에 강한 의구심을 품고 질문 폭탄을 퍼부었죠. 

"인터넷에 보니까 Steye (미국 다래끼약)를 쓰거나 베이비샴푸 면봉에 뭍여서 속눈썹 씻어주라던데 이건 필요 없는 것인지,  안약은 처방 안해주는지" 여쭤봅니다. 

"해도 나쁠 건 없겠지만 안약은 절대 필요없다." 면서 찜질이 짱이요 엄청 자신있게 말씀하셨어요. 

덧붙여 수술적인 방법으로 다래끼를 짜는 것도 안 좋다고 하시더라고요. 

그러면서 혹시 다래끼가 눈썹 area 까지 퍼지면 그건 감염이 확대된 것이니 그 때는 꼭 다시오라고 하셨습니다.


그리고 집에 가서 의사 샘 말씀대로 눈 찜질 하니까 으미 시원한 것 + 아픈 것이 싹 가라앉는 걸 보며 warm compression 에 따봉을 날렸습니다. 즉, 심한 다래끼가 아니라면 찜질로도 충분한 것 같아요. 물론 2-3일 해보고 안 되면 감염이 의심되는 경우라고 하니 항생제 연고를 써야한다고 합니다. 


그래서 저는 요새 의료대국 한국에서도 다래끼가 나면 그냥 수건 찜질만 합니다. 다래끼 난 분들, 이 방법을 이용해서 간편하게 다래끼 없애시길 바랍니다 ㅎㅎ

이케아 스톨파 / STOLPA 탁상 시계 사용기

분류없음

요즘 북유럽과 이케아에 꽂혀서 여러가지 잡동사니를 들이는 중입니다. 


그 중 하나가 이번에 사온 스톨파 / STOLPA 라는 시계인데요,

두꺼운 시계침과 숫자도 써놓지 않은 단순한 디자인의 노란색이 마음에 쏙 들어서 사왔습니다.


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/30sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

외관은 단순하죠. 어느 공간에나 잘 어울릴듯한 디자인입니다.

정말 외관만 보고 사온건데 사실은...


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/20sec | F/1.8 | 0.00 EV | 4.4mm | ISO-400 | Off Compulsory

안에 조도 센서가 있어서 주변이 어두우면 시계에 불이 은은하게 들어오더라고요. 영롱하죠? ㅋㅋ

위 사진은 밤에 불 다 끄고 깜깜할 때 찍은 건데 주변이 좀 밝게 나왔네요. 실제로는 완전 깜깜한 상태에서 찍었습니다.


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/30sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

밝을 때도 위에 전구 아이콘 부분을 터치하면 3초 가량 불이 들어왔다 나갑니다. 

의외로 얼마나 어둡냐에 따라 밝기가 자동 조정되더라고요.


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/60sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

건전지는 시계 작동을 위한 AA 전지 1개,

불을 위한 AA 전지 3개가 들어갑니다.


시계 건전지만 넣어도 잘 돌아가고요,

불빛을 임시로 차단하려면 위에 검정 스위치를 꺼주시면 됩니다.


조도 센서 작동 동영상

영상에서는 불이 미친듯이 깜빡거리는데요.. 정말 희한하게 제 G5 카메라만 갖다대면 저렇게 난리를 치더라고요.

촛불이 켜있어도 깜박거리는 현상이 가끔 있습니다.

물론 일반적인 어두운 상황에서는 같은 밝기로 계속 잘 켜져있어요.

센서가 아주 예민한 듯 싶습니다 ㅎㅎ 밝기 조정 기능 때문에 건전지도 오래 갈 것 같아요.


이 탁상시계(14cm) 말고 32cm 벽시계 모델도 있는데요, 그건 올 화이트에 검정 침입니다. 


여담) 모델명 오타

LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/30sec | F/1.8 | 0.00 EV | 4.4mm | ISO-150 | Off Compulsory

저는 한동안 모델명이 STOPLA 인지 알았어요. 시계 밑에 STOPLA 라고 오타가 나있더라고요. 이런 실수를 하다니...

저번에 어린이 장롱 조립하는데 구멍 잘못 뚫린거 와서 몇 시간 고생했던거랑

싱크대 설명서 엉뚱한게 들어와서 상판 날려먹을뻔 했던 경험 생각하면 오타는 애교라고 생각합니다... 애증의 이케아


제품 링크

14cm 탁상시계 (19,900원): https://www.ikea.com/kr/ko/catalog/products/90384065/

32cm 벽시계 (29,900원): https://www.ikea.com/kr/ko/catalog/products/30384068/


이케아 내부적으로는 가격대가 중고가(Medium and high)로 되어있던데... 

솔직히 품질이나 기능 생각하면 적절한 가격 아닐까 싶습니다. 

샤오미 필립스 천장등 사용기 + 알렉사 연동

분류없음

샤오미 필립스 천장등을 1년 정도 써보고 쓰는 후기입니다. 구입 당시에는 $75 였는데 요즘 큐텐 보니 많이 싸졌더군요.


사용기

우선 이 아이의 영롱한 자태를 보시죠.

LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/590sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

가장 낮은 색온도로 했을 때


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/774sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

가장 높은 색온도로 했을 때


평소에는 가장 낮은 색온도로 하고 있는데요, 색온도가 낮아지니까 확실히 방 분위기도 많이 아늑해졌습니다.


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/440sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

안에 여러보면 이렇게 여러개의 LED 로 구성되어 있습니다. 위에는 차가운 온도에 밝기 100%를 했을 때입니다.

LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/400sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

따뜻한 온도에 밝기 100%


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/60sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

차가운 온도에 밝기 1%


LG Electronics | LG-F700L | Not defined | Center-weighted average | 1/30sec | F/1.8 | 0.00 EV | 4.4mm | ISO-50 | Off Compulsory

따뜻한 온도에 밝기 1%




색온도/밝기 조절은 Mi Home 앱에서 가능하고요, 따로 리모콘을 살 수도 있습니다. 리모콘은 한 $15에 산 것 같은데 아주 예쁘고 유용해요

리모콘 안에는 수은 전지(CR2450)가 하나 들어가고 습도 온도 기록 기능이 있어요.

전지는 지금까지 한 번 갈았는데, 대략 6-8개월 가는 것 같습니다.

아담합니다. 벽 스위치랑 바꾸고 싶더라고요.


기록된 온도/습도도 위와 같이 Mi Home 앱에서 확인이 가능합니다. 방에 있는 샤프 공기청정기 센서와도 비교하는데 정확하더라고요. 

기록은 최근 한 달 것 까지만 확인 가능한 것 같습니다.


IoT 연동

알렉사와 연동도 가능합니다. 

그런데...

Yeelight 전구나 스탠드는 따로 Alexa Skill 이 있어서 에코와 직접 연동이 가능한데요,

샤오미 필립스 천장등은 전용 Alexa Skill 이 없습니다 ㅠㅠ


대신 Home Assistant 를 통해 연동이 가능한데요, 설치 방법 등에 대한 정보는 https://home-assistant.io/ 에서 알아보세요. 무료입니다 ㅎㅎ 

저는 라즈베리파이에 올려서 사용 중인데 일반 PC 에도 돌릴 수 있습니다.


**참고로 샤오미 필립스 천장등을 Home Assistant 에 올리려면 Mi Home 앱에서 토큰을 뽑아와야 합니다. 그런데 최신 버전에서는 토큰을 서버에 저장하는 듯 싶더군요. 그래서 설치된 Mi Home 앱의 데이터를 지운 다음 5.0.28 이전의 버전으로 다운그레이드 하셔서 여기(Retrieving the Access Token)에 나온 방법대로 토큰을 추출하셔야 합니다. 그리고 나서 여기(Xiaomi Philips Light - Home Assistant)의 가이드 대로 Home Assistant 설정 파일에 천장등을 추가해주시면 됩니다.


Home Assistant 에 천장등을 등록시켰으면 Home Assistant 알렉사 스킬을 활성화 시켜서 바로 이용이 가능합니다. 이 스킬을 사용하려면 Home Assistant Cloud 계정이 필요한데요, 이거에 연간 $60을 부과할 예정이라는 흉흉한 소문이 있습니다.


어쨌든 스킬 활성화시키면 알렉사 스마트 홈에서 아래 갈무리처럼 직접 제어도 가능합니다.


파이썬 객체를 파일로 저장하는 방법 - Serialization (직렬화)

공부/Python

파이썬 객체(Object)를 파일로 출력해서 필요할 때 마다 불러와 쓸 수 있는 방법을 소개한다. 진짜 간단함!


용어 정리: 직렬화와 역직렬화

쉽게 설명하면

직렬화 (Serialization) : 객체를 파일로 내보냄
역직렬화 (Deserialization) : 파일로 내보낸 객체를 다시 읽어들임


파이썬에서는 어떻게?

pickle 이라는 파이썬 표준 라이브러리를 사용하면 된다.


1. 직렬화 (파이썬에서는 pickling 이라고 주로 부른다.)

pickle.dump(내보낼 객체, 파일)



2. 역직렬화 (파이썬에서는 unpickling 이라고 주로 부른다.)

pickle.load(파일)



파이썬에서 Trie (트라이) 구현하기

자료구조

들어가며: "자동 완성" 기능에 대한 이야기


구글에서 어떤 단어를 검색창에 치면 그 단어로 시작하는 다른 키워드를 보여주는데요, 이런 걸 자동 완성(Autocomplete) 라고 합니다. 자동 완성을 구현 하려면 키워드들을 리스트에 담아서, 유저 인풋이 들어올 때 마다 인풋으로 시작하는 키워드를 리스트에서 빼오면 될 겁니다. 하지만, 만약 550만 개의 글을 갖고 있는 영어 위키백과의 제목들을 가지고 자동 완성을 구현하라고 한다면  (실제 일본 모 회사의 올해 인턴 모집 스크리닝 과제였음 ㅋㅋ) 리스트 따위로는 안 되고, 훨씬 더 효율적인 자료구조가 필요합니다. 오늘 배울 Trie 가 이럴 때 필요한 자료구조입니다.



Trie: 트라이? 트리? 기본적인 개념

우선 이름이 좀 특이한데요, Trie는 컴퓨터에서 검색을 뜻하는 retrieval 에서 온 단어라고 합니다. Prefix tree 라고도 하는데 직관적이고 부르기도 쉬워서 이렇게 부르는 사람도 꽤 있습니다. 아무튼 Retrieval 에서 왔으니 트리라고 불러야 하지 않냐!! 라고 하실 수도 있지만... 트리(Tree)와 혼동을 피하기 위해서 대부분은 그냥 트라이라고 부릅니다. 이런 논란에 휘말리기 싫어서 이 글에서는 Trie 라는 영어 명칭을 쓰기로 하겠습니다 큭큭

사실 Trie 는 트리(Tree) 자료 구조의 일종입니다. 미리 말씀드리자면 Trie 에서 어떤 문자열을 검색할 때의 시간 복잡도는 O(m) 밖에 안 됩니다. (m: 문자열의 최대 길이) 그래서 다른 자료 구조에 비해 문자열을 검색하는 데 특화된 자료 구조임을  알 수 있습니다. 


위 그림은 names = ['Joe', 'John', 'Johnny', 'Jane', 'Jack'] 을 Trie 에 삽입한 결과인데요.


각 Node 는 

(1) 글자를 갖는 key = 한 글자 (ex. J, A, C, K)

(2) 단어의 마지막 글자인지를 알려주는 flag (Terminal)

로 이루어져 있습니다. 


예를 들어 John 의 경우 J > O > H > N 으로 차례대로 Trie 에 삽입이 되었고 마지막 글자 N 의 노드에는 Terminal 이라는 flag 를 박아줍니다. 이 flag 때문에 John 이 한 단어임을 알 수 있습니다. 

Johnny 의 경우 John 까지는 이미 노드가 있으니 ( J > O > H > N )원래 있는 노드를 활용하고, John 뒤에 ny를 넣은 다음 y를 마지막 글자로 flag해주면 되는 겁니다.


파이썬에서 Trie 를 구현해볼까요?

첫번째. Node 구현



1. key 필드는 단어의 글자 하나를 담는 데 이용합니다. 영어라면 알파벳만 들어갈 수 있겠죠.

2. data 필드는 사실상 마지막 글자를 나타내주는 flag 입니다. 원래 boolean 형 으로 간단하게 true/false 로 구분할 수도 있지만

저는 마지막 글자인 경우에  단어 전체를 data 필드에 저장하고, 아닌 경우에는 그냥 널로 두려고 합니다. 그 이유는 밑에 구현할 starts_with 메소드 때문인데요, 곧 설명 드리겠습니다.

(즉, Jack 의 경우 J > A > C > K 에서 마지막 글자인 K 노드의 data 필드에만 "Jack" 이 들어가고, 그 외 노드의 data 필드는 널 (None) 으로 놔둡니다.)


두번째. Trie 구현

1. insert(string)

트라이에 문자열 삽입

2. search(string)

주어진 단어 string이 트라이에 존재하는지 여부 반환

3. starts_with(prefix)

주어진 prefix 로 시작하는 단어들을 BFS로 트라이에서 찾아 리스트 형태로 반환

마지막 글자 flag 를 boolean 으로 안하고 data 필드로 한 이유가 바로 이 메소드 때문인데요... 잘 생각해보세요. BFS 로 단어들을 찾을 때 결국 마지막에 우리에게 반환되는 것은 Terminal 노드, 즉 마지막 글자입니다. 그래서 단어 자체를 알려면 BFS 경로를 역추적해야 합니다... 이 과정이 귀찮기 때문에 ㅋㅋ Terminal 노드만 찾으면 BFS 역추적 할 필요 없이 단어가 뭔지 알 수 있도록 data 필드에 전체 단어를 넣어 놓은 것이죠.


위 세 가지만 구현하면 됩니다! 아래는 제가 간단히 파이썬으로 구현해본 Trie 입니다.

(파이썬 전체 코드는 여기를 클릭하면 보실 수 있습니다.)


끝!!

학교 자료구조 수업에서는 비중이 많이 있는 놈은 아니지만, 기술 면접이나 코딩 대회에서 많이 다루어진다고 하니 열심히 공부합시다!! 우리의 핵심 목표는 Trie다 하는 것으로 정신을 차리고 나아가면 취직할 수 있는 에너지가 분산되어 우주가 나서서 도와준다는 그런 마음을 가질 수 있을거다, 그렇게 생각합니다.

리스트 (List) - 2. LinkedList 구현

자료구조

이전 편에서 array 를 이용한 List 구현 방법인 ArrayList 에 대해 살펴보았는데요, 오늘은 다른 방법인 Linked List 에 대해 알아봅시다. 한국말로는 연결 리스트라고 합니다.


연결리스트란?

연결 리스트의 가장 좋은 비유 대상은 석탄 열차 아닌가 싶습니다. 요즘은 이런 기차가 많이 없어져서 모르는 분도 계실 것 같아 사진을 가져왔는데요.


석탄 열차는 맨 앞에 기사님이 타고 계신 차가 있고, 그 뒤로 석탄을 실은 차들이 서로에게 연결 되는 구조입니다. 연결 리스트에서는 데이터들이 저장되는 공간을 Node 라고 하고요, 기차의 한 량을 Node 라고 생각하시면 되겠습니다. 각 Node 는 데이터를 담아두는 data (석탄) 와 다음 Node 를 가리키는 next 포인터 (연결 고리)를 가지고 있습니다. 만약 석탄 차 하나를 더추가하려면 (add) 맨 뒤에 고리로 연결을 시키면 됩니다. 중간의 석탄 차 하나를 빼고 싶다면(remove) 빼고 싶은 차의 연결을 풀고 그 앞차와 뒷차를 다시 연결시키면 되고요.


N 개의 항목이 있는 연결 리스트의 예

비유로 알아봤으니 이제 진지한 정의를 해보겠습니다. 위 그림에서 연결 리스트는 리스트의 맨 앞(item 1)을 가리킵니다. 항목을 포함하고 있는 각각의 사각형들은 노드(node) 입니다. 노드는 크게 두 부분으로 이루어져있는데, 데이터를 담는 data 필드와 다음 노드를 가리키는 next 포인터입니다. 이 next 포인터들 덕분에 각각의 노드가 연결되는 것이라서 연결 리스트라고 이름 붙여진 것입니다. 각 노드의 next 포인터는 다음 노드를 가리키고요, 예외적으로 마지막 노드의 next 포인터는 다음 노드가 없으므로 null 을 가리키게 됩니다.




Node: 사실상 연결 리스트를 이루는 item

연결 리스트를 구현의 첫 단계는 Node를 구현하는 것입니다. Listnode 라는 Node를 아래에서 구현해보도록 하겠습니다.

Listnode 클래스에는 두 가지 필드가 있어야 합니다. data 필드와 next 필드이고요, 이 필드의 get/set 메소드들도 구현해야합니다. 

그리고 next 필드에 대해 첨언을 하자면, next 포인터는 다음 노드 그 자체인 Listnode 클래스를 가리키는 것이지 그 노드의 data 나 next 필드를 가리키는 것이 절대 아닙니다. 이 정도만 유의해두고 아래 실제 Java 구현을 보시죠~

<<< Listnode 구현 >>>


이제 노드도 구현을 해봤는데요, 이 노드들이 모여 어떻게 연결 리스트를 이루는지 알아보겠습니다. 우선 Listnode 하나를 만들고 시작합니다.


1. 이라는 Listnode 를 선언.

Reference 타입이기 때문에 아직 아무것도 가리키고 있지 않는 상태입니다.


데이터는 "ant", next 포인터는 null 인 Listnode 를 메모리에 할당, l 이 가리키게 함.

우리 LinkedList 의 첫번째 item 입니다.


"bat" 데이터를 가진 Listnode 를 생성하고, 

l 이 가리키고 있는 "ant" 노드의 next (다음 노드)로 "bat" 노드를 가리키게 함. 

"bat" 노드는 우리 연결 리스트의 두 번째 item이 되었습니다.


이렇게 두 개의 항목을 가진 연결 리스트를 만들어 보았습니다. 참 쉽죠? 

문제! 만약 두번째 노드의 data 를 "cat" 으로 변경하고 싶다면 어떻게 해야할까요? 두번째 노드.setData("cat") 일텐데... 문제는 "두번째 노드"를 어떻게 표현하느냐, 즉 두번째 노드의 reference 가 무엇이냐에 있을 것입니다. 

-> 연결 리스트에서 특정 노드의 reference 를 얻기 위해서는 무조건 처음 노드에서부터 시작해 원하는 노드까지 next 를 따라가며 찾아야 합니다. 


이 경우에는 다행히 두번째 노드라서 

l.getNext().setData("cat");

 으로 해결이 가능하지만, 


100번째 노드라면 

l.getNext().getNext().getNext()...반복...getNext().getNext() 

와 같이 알아낼 수 밖에 없습니다.


이건 좀 이따가 index 로 아이템 get 해오는 메소드 구현할 때 다시 이야기해보도록 하죠. ㅎㅎ


연결 리스트의 실제 구현 (Java)

노드도 구현했고, 노드가 모여 어떻게 연결 리스트를 이루는 지도 알아봤으니 아래에서 연결 리스트를 구현해보도록 하겠습니다.

연결 리스트의 연산 종류


1. add 메소드

기본적인 항목 추가인데요, 이미 연결 리스트가 주어져 있고 그 리스트에 n 이라는 임의의 Listnode 가 있다고 가정합시다. 또한 새 노드의 data 가 될 newdat 도 있다고 가정했을 때, n 이라는 노드 뒤에 새 노드를 어떻게 추가할지를 설명해보겠습니다.

연결 리스트에서 항목 추가의 메커니즘은 사실 간단하게 정리될 수 있습니다.

1. 주어진 데이터를 가진 새 노드를 만든다.

2. "연결!"

a. 새 노드의 next 포인터가 n 노드의 next 가 가리키고 있던 노드를 가리키게 한다.

b. n의 next 포인터가 새 노드를 가리키게 한다. 


이 과정을 그림으로 아래에 나타내보겠습니다.

1단계.  예제로 쓸 연결 리스트입니다. n 노드와 추가할 새로운 노드 보이시죠?

새로운 노드를 n 노드 뒤에 바로 추가할 겁니다.


2-a 단계. 추가할 노드의 다음 노드(next)를 정해줘야죠.

이제 n 노드의 next가 추가할 노드를 가리키게 해야합니다.

왜? 간단하죠!! n 노드 다음에 추가하는 노드이니깐요!


2-b 단계. ㅎㅎ 이렇게 해서 새 노드를 추가시켰습니다.

위의 과정에서 보셨듯이 연결 리스트에서 항목을 추가할 때는 노드 사이의 연결 순서를 알맞게 변경해주는 게 중요합니다. 반대로 항목을 삭제할 때도 고아가 되는 노드가 없게 순서를 잘 맞춰줘야겠죠. 이 과정을 이번에는 실제 Java 코드로 나타내면 아래와 같습니다. 

<<< 위 과정의 Java 코드 >>>

연결 리스트에서 add() 할 때의 시간 복잡도는 뭘까요? 1단계는 단순 메모리 할당이니깐 O(1), 2,3 단계에서는 결국 각 노드들의 필드들을 변경하는 거니깐 O(1)! 그래서 결국 시간 복잡도는 O(1)이라고 할 수있습니다. 다시 말해서 리스트에 수 만개의 항목이 있든, 한 개밖에 없든 간에 add() 에 걸리는 시간은 똑같다는 것이죠 ㅎㅎ

2. remove 메소드


리스트 (List) - 1. ArrayList 구현

자료구조

10일간의 추석 연휴 오예 신난다 동안 뭐를 할지 고민하다가 평소에 하겠다고 다짐만 하고 막상 하지는 못 했던 자료 구조 정리를 해보려고 합니다. 특히 제가 내년 초에 있을 기술 면접 대비도 해야 하기 때문에 기술 면접에서는 어떻게 등장할 수 있을지도 초점을 맞춰서 써보도록 하려고요. 제가 많이 부족하지만 벼락치기 대비에 유용한 가이드가 되었으면 좋겠습니다.


리스트

[정의] 순서가 있는 원소들의 집합 (An ordered collection of elements)

맨 처음으로 다룰 자료 구조는 리스트 추상 자료형(Abstract Data Type, ADT) 입니다.

여담: 순서가 없는 원소들의 집합은 Set! ㅎㅎ


대표적인 연산 종류

1. list()

생성자 constructor 입니다. 빈 리스트를 생성합니다.

필드에는 

(1) Object[] items (리스트 원소들의 array)

(2) int numItems (원소의 개수)

가 있습니다.

2. void add(Object ob)

리스트의 끝에 ob 라는 Object 를 추가합니다.

3. void add(int pos, Object ob)

리스트의 pos 위치에 ob 라는 Object 를 추가합니다. 

*** 이 과정에서 원래 pos 위치에 있던 원소부터 size() 위치(맨 끝)에 있던 원소들은 ob 를 위한 공간을 만들어주기 위해 한 칸씩 오른쪽으로 이동하겠죠?

(단, pos0보다 작거나 size() 보다 크면 에러)

4. boolean contains(Object ob)

ob 라는 Object 가 리스트에 있다면 true 를 반환합니다.  

5. int size()

리스트에 있는 원소의 개를 반환합니다.

6. boolean isEmpty()

리스트가 비어있으true 를 반환합니다.

7. Object get(int pos)

pos 위치에 있는 원소를 반환합니다. 

(단, pos  0보다 작거나 pos  size()면 에러, 0-indexing 이기 때문에 pos 는 size()보다 작아야겠죠.)

8. Object remove(int pos)

pos 위치에 있는 원소를 제거한 뒤 제거된 원소를 반환합니다. 

***  과정에서 pos+1 에 위치한 원소부터 size() 에 있는 원소들은 없어진 원소 공간을 채우기 위해 한 칸씩 오른쪽으로 이동합니다. 

(단, pos 가 0보다 작거나 pos  size()면  에러, 0-indexing 이기 때문에 pos 는 size()보다 작아야겠죠.)


리스트의 구현 방법

구현 1 - ArrayList
ArrayList 는 array 를 이용한 List 구현 방법입니다. 

1. 생성자
생성자는 필드들을 initialize 해서 빈 리스트를 생성해야합니다. 원소의 개수 필드인 numItems 는 0 로 설정되야하겠고요. 원소들을 담을 array인 items 에 조금 문제가 있습니다. items 를 null 로 initialize 하자니... 나중에 메소드 쓸 때 items 가 null 인 경우를 고려해야하니 문제가 될 것 같습니다. 해답은 바로 임의의 초기 크기를 가진 array 를 만드는 겁니다. 초기 크기 INITIAL_SIZE 는 static final 필드로 정하겠습니다.

우선 10을 INITIAL_SIZE 로 정하도록 합시다. 실제 사용 시에는 어떤 상황에서 리스트가 쓰이냐에 따라 적절한 초기 크기를 정해줘야 합니다.  바로 다음에 add 메소드를 구현하면서 느끼시겠지만, 초기 크기가 크면 공간이 부족해지는 상황 전에 add 를 많이 쓸 수 있다는 장점이 있습니다. 하지만 초기 크기를 너무 크게 잡아줘서 공간을 다 쓰지도 못하고 끝나는 메모리 낭비가 될 수도 있습니다. 결국 상황에 따라 적절한 INITIAL_SIZE 를 설정하는 게 중요하겠죠.

1-a. ArrayList 생성자

2. add 메소드
우선 파라미터가 하나인 add를 구현해보도록 합니다. 간단하게 Object 를 리스트의 끝에 추가시키면 되는 메소드입니다. 그런데 여기서 중요한 문제가 하나 있어요.

"array 에 더이상 공간이 없는 경우는?"

기존 array에 더이상 새 Object 가 들어갈 공간이 없다면 어떻게 할까요? 

(1) 더 큰 array 를 만들고, 
(2) 새 array 에 원래 있던 원소들을 복사를 하고 나서야

새로운 Object 를 추가시킬 수 있을겁니다. 게다가 이 문제는 파라미터가 하나인 add(Object ob)뿐만 아니라 파라미터가 두개인 add(int pos, Object ob)에도 해당되는 문제입니다. 그렇다면 (1) - (2) 과정을 하나의 메소드로 만들어버리면 두 add에서 재활용할 수 있겠죠. 그 새로운 메소드(array 크기 확장)를 expandArray 라고 이름 짓겠습니다. 그리고 이 expandArray는 유저가 알 필요 없는, 구현을 위해서만 사용되는 메소드이기 때문에 private 메소드로 만듭니다.

2-a. add(Object ob) 와 expandArray()


*** 맨 뒤에 항목을 추가하는 add의 시간 복잡도는 O(1) 혹은 O(n) 입니다. 이게 일반적인 표기인지는 잘 모르겠습니다만... (Oracle 에서는 Amortized O(1) 이라는 표현을 쓰긴 합니다만... 일반적인 Worst Case 시간복잡도하고는 다른 개념입니다) 뒤에 항목을 추가하는 연산 자체는 O(1) 이 맞습니다. 하지만 우리가 크기를 지정해줘야하는 array를 쓰고 있어서 가끔 공간이 부족해 expandArray() 를 해줘야하는 상황이 있잖아요. 이 공간 확장의 경우 때문에 O(n) 의 상황도 생기는 것입니다.

이제 파라미터가 두 개인 add(int pos, Object ob) 의 경우를 살펴볼게요. 방금 구현한 파라미터 한 개인 add에 비해 파라미터가 하나 더 늘었다는 차이밖에 없는데요, 중요하게 점검할 부분이 있습니다.

"pos 에 올바르지 않은 값이 입력되는 경우"

pos 의 값이 올바르지 않은 경우(예를 들어 음수나 array size 보다 큰 수)에 예외처리를 해줘야합니다. 따라서 
(1) pos 가 올바른 범위에 있는지 확인합니다. 올바른 범위가 아니면 IndexOutOfBoundsException 을 throw 합니다. 
(2 pos 가 올바른 값이었다면, array 에 공간이 있는지 확인해야합니다. 공간이 없다면 expandArray() 를 호출해 array크기를 확장합니다.
(3) 그 다음, pos ~ numItems-1 까지 위치의 모든 item 들을 오른쪽으로 한 칸씩 이동시켜서 새 Object 가 들어갈 공간을 만들어줍니다. 
(4) 이 모든 과정을 한 뒤에야 새 Object 를 pos 위치에 삽입하고, numItems에 1을 더합니다.

2-b. add(int pos, Object ob)

*** 특정 위치에 항목을 추가하는 add의 시간 복잡도는 O(n) 입니다. 맨 뒤에 추가하는 add 와는 다르게, array 를 traverse 해야하기 때문이죠.



3. remove 메소드
remove 메소드 에는 지울 원소의 위치가 파라미터로 주어집니다. 그래서 
(1) 먼저 pos 가 올바른 범위에 있는지 확인을 해야합니다.
(2) void 가 아니고 지운 Object 를 반환해줘야 하기 때문에 지우기 전 지울 Object 를 임시로 저장해줘야합니다. 
(3) 그 다음 pos+1 ~ numItems-1 까지 위치의 원소들을 왼쪽으로 한 칸씩 이동해줍니다. 이 과정에서 우리가 지워야 하는 items[pos] 는 덮여쓰여지기 때문에 사실상 지워진 것이 됩니다. 
(4) 그리고 깜박할 수 있는 것 하나! 마지막 원소가 있던 원래의 자리에는 그대로 그 항목이 있습니다.

예를 들어

 pos

item 0

item 1 

item 2 

item 3

item 4 

item 5 

 value

d

e

f


와 같은 array 있을 때 item 3 을 지웠다고 합시다. 그러면 아래와 같이 한 칸씩 왼쪽으로 이동시킵니다.

 pos

item 0

item 1 

item 2 

item 3

item 4 

item 5

value

e

f

f



그런데!! 5 위치에 이동시킨 원래 항목이 그대로 남아있습니다!! 따라서 이 마지막 항목이 원래 있던 위치의 항목(이 경우 item 5)도 삭제해줘야 합니다.

(5) 그리고 나서 numItems 에서 1을 빼고, 지운 Object 를 반환해주면 되는거죠.

3-a. remove(int pos)

*** remove의 시간 복잡도는 O(n) 입니다. ob 를 위한 공간을 만들 때의 for loop 때문이죠. 

4. get 메소드

주어진 위치에 있는 항목을 반환해주면 되는데요 ㅎㅎ 쉽습니다. 
(1) pos 가 파라미터로 들어오니깐 범위 체크 해주고요, 
(2) 그 다음 items[pos] 반환해주면 끝! 

4-a. get(int pos)

get의 시간 복잡도는 O(1) 인데, 그 이유는 array 를 사용하는 것에서 찾을 수 있습니다. array access 는 메모리 시작 주소에 pos 위치를 더하기만 함으로써 access 하는 것이거든요. 결국 access = 사칙 연산이라고 볼 수 있기 때문에 O(1) 이라고 이해하는 것은 어떨까요?


5. iterator 메소드
어떤 자료 구조를 쓸 때 그 자료 구조를 iterate(순회) 할 일이 많습니다. 방금 구현한 get 메소드를 이용해서 우리의 ArrayList 를 순회하는 iterator 라는 것을 만들 수 있는데요, 중요한 포인트가 많으니 유심히 보도록 합시다. 되게 쉬운 개념인데 저는 자료 구조 수업 들을 때 신경 별로 안쓰다가 간단한 Iterator 문제에서 많이 틀렸었습니다 ㅠㅠ

자바에서 iteratorCollectionimplements 하는 모든 자료 구조에서 필요한 메소드입니다. Iterator 인터페이스는 java.util 에 정의되어 있고요, iterator 메소드는 이 Iterator 을 반환해줘야 합니다.

Iterator 은 리스트의 항목을 가리키고 있는 손가락 혹은 포인터처럼 생각하시면 쉽습니다. 

(1) 처음 Iterator 가 생성되면, 그 Iterator가장 첫번째 항목을 가리니다. 
(2) Iterator 에는 next 라는 메소드가 있는데, 이 next 라는 단어가 여러분을 혼란스럽게 할 수 있으니 조심하세요. 
(3) next 메소드는 현재 Iterator가리키고 있는 항목을 반환함과 동시에 포인터(손가락)가 다음(next) 항목을 가리키게 합니다. 절대로 다음 항목을 반환하는 게 아닌거죠... 현재 가리키고 있는 항목을 반환하는 겁니다!! next 라고 이름 붙여진 것은 호출되면 다음 항목을 가리키기 때문에 입니다.


Iterator 가 어떻게 작동하는지 예제와 함께 알아봅시다. 아래와 같은 Fruits 리스트가 있습니다.

사과 

배 

바나나 

딸기 

 


1. 이 리스트에 대한 Iterator 을 생성하면, Iterator 은 아래처럼 맨 처음 항목인 사과를 가리키게 됩니다.

사과 

배 

바나나 

딸기 

 

 


 


 

2. 이제 next 를 호출하면 현재 가리키고 있는 사과를 반환하고, 리스트의 Iterator 은 아래처럼 다음 항목인 배를 가리키게 됩니다.

사과 

배 

바나나 

딸기 

 

 

 

 


 

3. next 를 호출 -> 배를 반환.  포인터는 바나나를 가리킴.

사과 

배 

바나나 

딸기 

 

 

 

 


 

4. next 를 호출 -> 바나나를 반환.  포인터는 딸기를 가리킴.

사과 

배 

바나나 

딸기 

 

 

 

 

 
5. (주의) 위와 같은 상황의 Iterator 의 hasNext 메소드는 true 를 반환합니다! 왜? 아직 access 하지 못한 항목이 하나 남았잖아요. 다시 한번 next 를 호출하면 딸기를 반환할 것이고 그제야 아래와 같은 그림이 됩니다.

사과 

배 

바나나 

딸기 

 

 

 

 


 

6. 이제 iterator 가 리스트의 바깥으로 나가버렸습니다. 이제 hasNext 메서드는 false 를 반환하고요, 이 상황에서 next 를 호출하면 NoSuchElementException 응~그런거없어 에러를 뿜는 걸 볼 수 있을겁니다.



아래는 우리 List 클래스를 위한 Iterator 을 구현에 대한 설명입니다.


우선 ListIterator 라는 새로운 클래스를 만들건데요, 두 가지 필드가 있습니다. 

(1) iterate(순회) 될 List

(2) 포인터(손가락) -- 인덱스라고 합시다 ㅎㅎ


그리고 우리의 ArrayList 클래스에서는 아래처럼 iterator 라는 메소드를 만들어줘야 합니다. 이 메소드는 ListIterator 의 생성자를 호출해 자기 자신을 List 에 반환합니다.


아까 Iterator 라는 인터페이스가 있다고 말씀드린 거 기억 나세요? ListIterator 은 이 인터페이스를 implements 하기 때문에 세 가지 메소드를 구현해야합니다. 아까 봤던 hasNext 와 next, 그리고 선택적으로 remove 라는 메소드를 구현해야 합니다. 아래에 간단히 구현한 ListIterator 클래스를 참고해주세요.


6. contains 메소드
contains 는 파라미터로 들어온 Object 가 List 가 존재하면 true 를 반환합니다. items 를 순회하며 원하는 Object 가 있는지 비교해가며 체크하면 됩니다. 아래 구현을 보시죠.

7. isEmpty 메소드
ㅎㅎ 이제 막판이네요. isEmpty 는 List 가 비어있는지 여부를 반환합니다. 즉 List 원소 개수가 0이면 true 를 반환합니다.

8. size 메소드
size 는 List 원소 개수를 반환합니다. 지금까지 많이 호출했던 메소드인데 ㅋㅋ 이제야 구현하는군요.


구현 2 - LinkedList


이번 글에서는 array 를 이용한 List 구현인 ArrayList 를 알아봤는데요, LinkedList 라는 구현 방법도 있습니다. 다음 편에서 알아보도록 하겠습니다.



[IoT] 시골 집에 IoT 구축하기 - 1. 만능 리모콘 (Universal IR Remote)

분류없음

최근에 아버지가 시골집을 짓고 계셔서 부엌도 맞추고, 가전제품도 사는 중입니다. 저는 아버지처럼 집 짓는 것에 능력이 없는지라.. 제 전공으로 새 집에 기여할 방법을 찾아봤습니다. 역시나 요즘 핫한 IoT 환경을 집에 구축하는 게 좋을 것 같더군요. 


우선 제 청사진은 이렇습니다. 문을 열고 집에 들어가면 거실에 있는 조명이 똭~ 켜지고 에어컨, TV 등 가전제품도 켜집니다. 기계 목소리로 "환영합니다!" 까지 나오면 저는 껌뻑 죽을 듯 하네요. 또 외출 시에는 집 안 모든 전기 제품들의 전원이 꺼지고, 방범 모드가 활성화 되어서 침입자가 있을 때에는 사이렌이 위용~위용하고 제 핸드폰으로 해당 상황의 영상이 푸쉬되는 거죠. 방범 기능 빼면 솔직히 필수적인 기능은 아닙니다만... 괜히 아이언맨의 스타크 회장이 된 듯한 느낌을 들 것 같아서요...


사진 출처: Philips UK


이런 IoT 환경을 구축하기 위해서는 IoT 기능이 있는 전구, 가전 제품 등을 싹 다시 사야합니다. 조명의 경우는 Philips Hue (위) 가 유명하고, 가전 제품은 삼성의 SmartThings (아래) 나 LG의 SmartThinQ 가 있겠네요. 필립스 휴 같은 경우에는 제가 예전부터 꼭 사고싶었던 제품이기도 합니다.


출처: Samsung Newsroom


그런데 제가 언급한 메이저 업체에서 나오는 IoT 기기들은 가격이 상당히 비쌉니다... 일례로 전구 3개에 브릿지 1개를 주는 필립스 휴의 스타터 킷은 다나와 최저 가격 기준 126,050원 으로 뜹니다. IoT 기능이 적용된 가전기기도 "스마트" 라는 이름을 붙여서 소비자들을 등쳐먹는다는 생각밖에 안들고요. 예컨대 냉장고에 IoT 기능이 제 관점에서는 얼마나 필요할지 잘 모르겠습니다.


그런데 한국 전자기기 커뮤니티 등을 열심히 검색해 본 결과, 가성비의 제품을 많이 발견했습니다. 가성비 전자제품이라 하면 익히 알고 계실 기업일 것 같은데요... 바로 샤오미입니다. 또 Broadlink 라는 중국 기업도 유명하더라고요. 샤오미 같은 경우에는 IoT 기능이 내장된 가전 제품도 많이 출시합니다. 공기 청정기, 에어컨, TV 그리고 자전거 까지 수많은 제품이 있는 것으로 알고 있습니다. 그런데 이미 쓰던 제품이 있는 경우 샤오미로 바꾸긴 좀 그렇죠. 이런 경우에 필요한 제품이 샤오미 만능 리모콘과 같은 만능 리모콘입니다. 적외선 리모콘으로 동작하는 어떤 가전 제품에도 사용할 수 있으니, 에어컨, TV 는 기본적으로 해당하지요. 


우선 아래 Broadlink 의 만능 리모콘을 한번 볼까요.



이렇게 생겼는데요, 안에는 적외선 transmitter 가 여러 방향으로 달려 있어서 다양한 방향으로 리모콘 신호를 보내는 것이 가능합니다. 다만 각 가전제품의 리모콘으로 적외선 신호를 버튼마다 학습을 시켜야 쓸 수 있기 때문에 초기 설정이 귀찮을 수도 있습니다. 제가 듣기로는 샤오미의 것은 서버에 저장된 설정들이 있다고 하더라고요. 어쨌든 이렇게 학습을 시킨 브로드링크는 핸드폰 앱을 이용해서 작동시킬 수 있습니다. 혹시 컴퓨터와 친하신 분이라면 비공식 API 를 이용해서 구글 어시스턴트같은 인공지능 스피커에 연동시킬 수도 있습니다. 저 같은 경우에는 브로드링크-IFTTT-구글 어시스턴트 이런 식으로 IFTTT를 브리지로 활용하고 있습니다.


"나는 이런 브리지가 필요없다! 네이티브같은 접근을 원한다!!" 하시는 분은 broadlink 파이썬 모듈을 이용해보시는 것도 좋습니다. 공식 제작된 모듈은 아니고요, 이 역시 일반인이 제작해서 github 에 올린 것인데 꽤 잘 작동합니다. (링크: https://github.com/mjg59/python-broadlink ) 깃헙을 잘 찾아보시면 다른 언어/플랫폼 용으로 제작된 것도 많이 찾으실 수 있답니다. 




제 단기적인 목표는


1. 일정 온도 이상 높아졌을 때 에어컨 켜기

2. 좀 시원해지면 에어컨 끄기

3. 집 도착 500m 전 에어컨 켜기

4. 집에 사람이 없으면 에어컨 끄기


이 정도로 잡고 연습(?) 중입니다. 문제는 정말 0.01% 확률로 리모콘이 작동이 안 되는 경우가 있는데, 그럴 경우 슈뢰딩거의 고양이처럼 에어컨의 작동 상황을 모른다는 것입니다. 이건 추후 방법을 생각해내지 않으면 전기세 폭탄이 뻔하기에... 걱정이 되긴하네요.



미국 학생식당에 질린 나를 위한 식료품점 트레이더 조 (Trader Joe's)

위스콘신 매디슨

저희 학교는 캠퍼스가 아주 넓다 보니깐 학생식당이 6개 정도가 있습니다. 식당 마다 메뉴가 살짝 다르긴 하지만 결국 제가 사 먹는 것은 샌드위치, 피자, 치킨윙, 아시아 음식이라고 포장된 정체불명의 음식 밖에 없는 것 같네요. 그러다보니 수업 중간에 집에 들러서 들기름 팍팍 넣은 간장밥, 고추참치밥, 고추장밥 (aka 환상의조합) 해 먹는 것이 비싼 식당 안 부러울 정도에요.


물론 저런 환상의 조합들도 물리기 마련인데, 그래서 저는 한 달에 한번은 꼭 트레이더 조 (Trader Joe's) 라는 식료품점에 갑니다. 한국에서도 유기농 음식 열풍이 분 적이 있었는데, 미국에는 유기농 음식만을 전문적으로 하는 식료품 점들이 많아요... Whole Foods Market 이라는 악명 높은 체인도 있죠. 트레이더 조도 일부는 유기농 식료품점으로 알고 있는 경우가 있는데, 모든 상품이 유기농인건 아닙니다. 특히 가격이 홀푸드같은 바가지 수퍼마켓보다 훨씬 저렴해서 즐겨 찾아요.


그래서 제가 트레이더 조에서 즐겨 찾는 몇가지 상품들을 소개해보고자 합니다. 제가 사는 곳은 위스콘신 매디슨이고요, 이 곳의 트레이더 조는 UW 캠퍼스에서 얼마 멀지 않아요.


1. 냉동 아시아 음식들 (한국 음식도 있음!!)

Huawei | Nexus 6P | 1/60sec | F/2.0 | 4.7mm | ISO-100 | Flash did not fire

아시아 음식 중에 가장 먼저 소개할 것은 트레이더 조의 베스트 셀러!! Mandarin Orange Chicken 이에요.

진짜 중국 요리는 아니고요, 우리나라 짜장면 처럼 현지에 맞춰진 중화 요리라고 보시면 됩니다.

3인분 정도 되는데 5불 밖에 안해요.


제가 친구 생일 파티에 이거 가져가서 거의 10분 만에 전자레인지하고 냄비만 써서 요리 끝냈던 적이 있어요. 

친구들이 너무 맛있게 먹더라고요. 저도 맛있었습니다.


사실 치킨 자체도 이미 익혀져 있는 것 같고, 그냥 heat up 시키는 개념이기 때문에

누가 요리하든 맛없을 수가 없어요.


오렌지 치킨 옆으로 여러가지 다른 요리들이 보이는데요, 

다 먹어보지는 못했지만 대부분 다 맛있어요.



Huawei | Nexus 6P | 1/60sec | F/2.0 | 4.7mm | ISO-97 | Flash did not fire


아아.. 대망의 한국 요리...

비빔밥하고 파전이 있는데요,


비빔밥은 전자레인지에 돌려 먹는 것이고

파전은 팬에 기름둘러서 5분 정도 익혀주면 됩니다.


둘다 대박 맛있어요. 가시면 꼭 드셔보셔야 할 음식들입니다.


Huawei | Nexus 6P | Not defined | Unknown | 1/24sec | F/2.0 | 0.00 EV | 4.7mm | ISO-625 | Flash did not fire


비빔밥 실물을 한번 찍어봤습니다.


시금치 당근 계란이 고명으로 있고요, 고추장도 들어가 있어요.

고기도 꽤 리얼한 고기가 4-5 점 들어있다는 게 감동


그런데 살짝 비싸요 ㅠㅠ 3.5불인가?

그래도 비빔밥 하나 만들자고 야채 사고 계란 부치고 할 바에야

저거 먹는게 나아요.


맛은 비행기 기내식으로 나오는 비빔밥 이랑 비슷합니다.



2. 냉동 인도 커리


아... 사진이 없네요 옛날에 친구한테 보냈던 사진으로 대체.




냉동 코너에 가면 인도 커리 팝니다. 

꽤 맛있어요. 전자레인지에 돌려먹으면 되는건데, 


빵 코너 가서 난 사가지고 마지막 1분 전자레인지 돌릴 때

구멍 뽕뽕 뚫린 필름 뚜껑 위에 난 넣고 돌리면

아주 따뜻하고 찰진 난을 먹을 수 있어요


동남아 커리도 있던데 안먹어 봐서 모르겠습니다...



3. 신선한 과일/야채


음?? 사진이 없어서 인터넷의 이미지로 대체.

(출처: http://www.ambitiouskitchen.com )



완전 싸다~ 이렇게 말할 수는 없고요,

적당한 가격에 꽤 괜찮은 질의 과일들을 팝니다.

가끔 블루베리/딸기/포도 같은 거 사먹는데 맛있어요.


야채도 있어요. 저는 주로 마늘/양파/당근/시금치 등을 여기서 삽니다.

저는 트레이더 조 양파가 이상하게 맛있더라는...


4. 유제품


Huawei | Nexus 6P | Not defined | Unknown | 1/120sec | F/2.0 | 0.00 EV | 4.7mm | ISO-71 | Flash did not fire


또 자랑스러운 우리동네 위스콘신이 치즈로 유명한 거 아니겠습니까 ㅋㅋ

치즈 종류도 엄청 많고, 가격도 쌉니다.


wisconsin cheese에 대한 이미지 검색결과


모든 치즈 (hard/soft) 다 맛있는데, 저는 특히

Brie 라는 치즈가 맛있더라고요. 크래커에 얹어 먹으면

마치 버터같은 질감에 크림 같은 맛이 납니다.



5. 과카몰레!


미국 사람들 파티할 때 꼭 아보카도랑 나초 칩 놓잖아요.

과카몰레 중독증이라고 주장하는 사람들도 많고.


그런데 저는 미국에서 친구가 과카몰레 만들어줘서 처음 먹어봤을 때

그 맛이 너무 이상했습니다. 그래서 아보카도 때문인가 했는데,


언젠가 트레이더 조 갔을 때 시식에 과카몰레가 나와있던 거에요. (매일 시식메뉴가 바뀝니다)

호기심에 먹었는데 예전에 친구가 해줬던 과카몰레랑 다른 맛이...


이름은 Avocado's Number 입니다. 아보가드로 수를 이용한 언어 유희. 솔직히 노잼 그런데 미국 사람들이 저런 노잼 개그를 좋아해요 에휴



이렇게 생겼고, 저거 말고도 무슨 Spicy Guacamole 인가도 있는데 저는 맛이 없더라고요. 

아보카도's 넘버랑 차이점이 아마 바질,토마토,고추 같은게 들어갔는지의 차이인 것 같습니다.


암튼 다른 과카몰레 말고 아보카도's 넘버 과카몰레를 사세요.



대충 이 정도가 제가 트레이더 조에서 즐겨 찾는 음식들이었습니다. 


++ 제가 저번 주 다녀왔는데 카운터 옆에 허니버터칩이 있어서 사와서 먹어봤습니다. 한국 허니버터칩이랑 비슷한 맛에 덜 느끼해요. 가격도 엄청 큰 봉지가 2불인가 하니깐 혹시 재고 있으면 하나 사보시기를 권합니다!!! 이름도 Honey Butter Chips!