bookmark_border좁은문

앙드레지드, 제롬과 알리사

[소설]좁은문/앙드레지드 – 사랑의 애절함과 답답함 사이

일찍이 아버지를 여읜 제롬은 방학 때마다 두 살 위인 알리사와 한 살 아래의 줄리엣, 이 두 외사촌이 있는 삼촌 집에 내려가 함께 살았다. 알리사는 정숙한 반면 줄리엣은 말괄량이였다. 알리사의 어머니는 바람기가 있는 여인으로서, 행복한 가정을 버리고 젊은 장교와 놀아났다. 그 뒤로 알리사의 신앙은 깊어졌고, 보다 청순한 것을 찾게 되었다. 제롬은 주일 예배 때 알리사와 더불어 들은 설교를 평생 잊을 수가 없다. ”좁은 문으로 들어가기를 힘쓰라. 멸망으로 인도하는 문은 크고 그 길이 넓어 들어가는 자가 많고, 생명으로 인도하는 문은 좁고 협착하여 찾는 이가 적음이니라.”

제롬은 모든 괴로움과 슬픔을 넘어 하나님의 길에 이르듯이 노력한다면 알리사와의 사랑에 결실을 가져오게 되리라 믿었다. 쾌활한 줄리엣은 알리사와는 정반대의 성격이었다. 몰래 제롬을 사랑하고 있지만, 반항적으로 다른 남자와 결혼하고 만다. 제롬은 알리사에게 사랑을 고백하지만, 알리사는 하나님 안에서 하나가 되자고 대답한다.

제롬은 군에 입대한다. 그리고 알리사에게 사랑의 편지를 보내고, 알리사도 여기에 대해 답장을 보낸다. 그러나 만나서 결혼을 종용하면, 알리사는 “우리는 행복을 위해서가 아니라 거룩함을 위해서 태어난 것입니다”고 대답하여 제롬을 실망시키는 것이었다. 편지 속의 알리사와 현실의 알리사가 다름에 제롬의 마음은 피곤해진다. 제롬은 알리사를 단념하고 3년의 세월을 보낸다. 오랜만에 둘이는 다시 만나게 되지만 알리사는 너무나 정결한 존재였다. 그녀는 스스로 지상의 사랑을 버리고 ‘좁은 문’을 거쳐 행복에 이르는 길을 걸으려 하고 있었다.

그날 밤, 알리사는 수정 목걸이를 걸지 않고 있었다. 제롬은 쓸쓸한 마음으로 알리사의 곁을 떠났다. 알리사는 “내가 수정 목걸이를 걸지 않고 만찬에 나오면 아무 말도 하지 말고 돌아가 주세요”라고 미리 선언했던 것이다. 그 뒤, 제롬은 알리사가 요양원에서 숨진 사실을 줄리엣의 편지를 통해 알게 된다. 알리사의 일기에는 “하나님이시여, 다시 한 번 그분을 만날 수 있도록 하여 주옵소서”라는 구절을 비롯해, 몹시도 제롬을 사랑했지만 ‘좁은 문’인 하나님에의 봉사 때문에 고민한 사실이 기록되어 있었다.

[네이버 지식백과] 좁은 문 (세계문학사 작은사전, 2002. 4. 1., 김희보)

bookmark_border건강한 생활

  1. 워밍업 (몸에 열을 내기)
    1. 가벼운 스트레칭
    2. 러닝머신 (5분~10분)
  2. 근력운동
    1. 3세트
    2. 세트간 휴식 1분~4분, 숨 찾으면 바로 시작
    3. 매 세트마다 반복불능까지
  3. 쿨다운 (정리운동)
    1. 가벼운 걷기 (5분)
    2. 정적인 스트레칭

상체 운동

  • 등 (광배근)
    1. 렛풀 다운
    2. 철봉 (풀업) – 발은 뒤로, 밴드 이용
    3. 케이블 로우
    4. 로잉 머신
  • 가슴 (대흉근)
    1. (누운) 체스트 프레스 머신
    2. 팔굽혀 펴기
    3. 덤벨 프레스
    4. 펙 플라이
    5. (앉은) 체스트 프레스 머신
  • 삼두
    1. 딥스 – 귀와 어깨를 멀리, 벤드 이용, 웅크린 자세로
    2. 덤벨 킥백 – 허리 아픔 의자 이용
    3. 벤치 딥스
  • 어깨
    • 숄더 프레스 머신
    • 사이드 레터럴 레이즈 – 높지 않게. 약간 앞쪽으로.약간 숙이고
    • 덤벨 숄더 프레스
    • 프론트 레이즈 ㅡ 전면
  • 이두
    1. 암컬 머신
    2. 덤벨 컬
    3. 해머 컬
    4. 스탠딩 바벨 컬

하체 운동

  • 하체
    1. 다리로 밀기 (레그 프레스) – 무릎을 적게 구부리기
    2. 레그 익스텐션
    3. 레그 컬 – 발목을 펴서 제끼고, 과도한 중량시 요통
    4. 짝다리 스쿼트 (스플릿 스쿼트) – 사물을 잡고 균형, 힘을 더 주려면 발을 멀리
    5. 스쿼트 – 하프 스쿼트, 엉덩이 뒤로 빼는 스쿼트
    6. 런지 – 약간 벌리고 좀 멀리, 뒷꿈치 들고 상체 약간 앞으로 숙이고, 앞다리 뒷꿈치 중심, 숙이는 다리 무게 안가게, 무릎 조심, 뒷쪽은 보조. 엉덩이 햄스트링 사용한다고 생각하고 접히게
  • 엉덩이
    1. 다리 벌리기 – 밴드 이용
  • 종아리
    1. 발꿈치 들기 – 천천히, 발 닿기전 멈춤, 엄지발가락 힘
  • 복근
    1. 다리 들기 (벤치 레그 레이즈) – 요추전만, 허리 수건
    2. 프랭크 – 요추전만 유지, 목 들어줌

# 미는 운동

  • 가슴 (대흉근)
    1. 앞으로 밀기 (체스트 프레스) : 머신, 고무밴드 이용
    2. 덤벨 프레스
    3. 스미스 머신 벤치 프레스
    4. 팔굽혀펴기, 병합팔굽혀펴기
  • 어깨
    1. 숄더 프레스 머신
    2. 덤벨 숄더 프레스
    3. 밀리터리 프레스 (스미스머신)
    4. 사이드 레터럴 레이즈 – 높지 않게. 약간 앞쪽으로.약간 숙이고
    5. [벤트오버 레터럴 레이즈] – 후면삼각근
    6. 프론트 레이즈 ㅡ 전면
    7. 견갑골 딥스
    8. 뒤로 날개짓(리버스 플라이(인클라인덤벨,머신))
  • 삼두
    1. 딥스 – 귀와 어깨를 멀리, 벤드 이용, 웅크린 자세로, 링딥스 추천
    2. 견갑골 딥스
    3. 덤벨 킥백 – 허리 아픔 의자 이용
  • 복근
    1. 다리 들기 (벤치 레그 레이즈) – 요추전만, 허리 수건
    2. 프랭크 – 요추전만 유지, 목 들어줌
    3. [크런치]
  • 엉덩이
    • 다리 벌리기 – 밴드 이용

# 당기는 운동

  • 등 (광배근)
    1. 렛풀 다운
    2. 철봉 (풀업) – 발은 뒤로, 밴드 이용
    3. 로잉 머신
  • 이두
    1. 암컬 머신
    2. 덤벨 컬
    3. 스탠딩 바벨 컬
    4. 해머 컬
  • 하체
    1. 레그 프레스 – 무릎을 적게 구부리기
    2. 레그 익스텐션
    3. 레그 컬 – 발목을 펴서 제끼고, 과도한 중량시 요통
    4. 발꿈치 들기 – 천천히, 발 닿기전 멈춤, 엄지발가락 힘
    5. 스쿼트
      1. 하프 스쿼트, 엉덩이 뒤로 빼는 스쿼트
      2. 고개약간 들고 허리세우고 무릎이 먼저 나가지 않고 힙을 뒤로 빼면서 골반이 수축되게
      3. 무릎의 힘이 아니라 골반 허벅지 윗쪽 힘으로 오르기
      4. 무게중심 발 뒤꿈치. 발바닥 바깥힘
    6. 짝다리 스쿼트 (스플릿 스쿼트) – 사물을 잡고 균형, 힘을 더 주려면 멀리
    7. 런지
      1. 약간 벌리고 좀 멀리
      2. 뒷꿈치 들고 상체 약간 앞으로 숙이고
      3. 앞다리 뒷꿈치 중심
      4. 숙이는 다리 무게 안가게 무릎 조심
      5. 뒷쪽은 보조. 엉덩이 햄스트링 사용한다고 생각하고 접히게
    8. [케틀벨]

  • 운동시 허리 유의 단계
    • 1단계 : 최대 요추전만
    • 2단계 : (아프면) 가동범위 줄이기
    • 3단계 : (아프면) 무게 줄이기
  • 엎드려 신전동작
    • 1, 2, 3, 4 단계
    • 2단계 추천 :
      • 손을 야구배트 잡듯이 잡고 턱을 굄
      • 힘을 완전히 빼고 코로 숨마시고 입으로 숨을 내쉴때 오무리고 천천히 끝까지 뱉어냄
      • 들어마실때 아프면 피하고 단계 낮춰야, 방사통도 마찬가지
  • 운동중, 운동후, 다음날 허리 아픈정도 관찰 필요
  • 허리 운동
    • 허리 1단계 : 요추전만(자연복대위험) 걷기
    • 허리 2단계 : 1단게에 플러스, 다리벌리기, 아래당기기, 뒷꿈치들기
    • 허리 3단계 : 2단계에 플러스, 플랭크, 팔굽혀펴기, 레그프레스 무릎을 적게 구부리기, 엉덩이 뒤로 빼는 스쿼트, 턱걸이 다리위치 주의, 뒤로 날개짓(리버스플라이),체스트프레스
  • 허리 근력
    • 유연성 집착마라. 허리구부리는 스트레칭은 위험
    • 허리근력강화운동 안된다. 자연복대도 위험
    • 통증에 귀를 기울여라. 처음 시원하고 3, 4 시간 후 또는 다음날 아프다면 운동 그만
    • 중심통증만 좋은 통증
  • 목디스크
    • 승모근 강한 훈련 안됨
    • 과도한 외력 목디스크 찢어짐
    • 턱당김 안됨
  • 맥켄지 신전동작
    • 허리 세우고 양팔 어깨 뒤로 젖히고 목을 뒤로.
    • 허리 통증이 발로 뻗어가면 나쁜것 중지. 가운데만 좋은 통증
  • 운전 허리 목 쿠션 목이 뒤로 유지되도록
  • 운동이란 근육, 과부하, 휴식
  • 배측신경절 염증은 뒤로 제끼면 더 방사통을 강하게 느낌 이건 염증 치료 필요

  • 대표적 기초 근력운동 : 체스트프레스 렛풀다운 레그익스텐션
  • 전완근
    • 악력, 버티는근육임
  • 일정한 시간에 운동
  • 부상이 없도록 모든 운동은 저중량에서부터
  • 휴식일에 마사지, 폼롤링, 벨런스 운동
    • 터키쉬겟업 : 아령.커틀벨 들고 일어서기
    • 버드독
    • 유산소 운동 짧고 굵게 뛰고 쉬고 반복 30분 이내
    • 야외로 나가라
  • 복근 코어운동 롤아웃(ab슬라이드) 가까이부터 점점 멀리 팔꿈치를 약간 구브린 상태에서
  • 허리 운동
    • 사이드라잉힙업 – 높지않게 시작 최대 45도
    • 프론트 플랭크 위드 힙 익스텐션 – 수평유지 3초간 멈춤
    • 힙 브리지 – 과하게 올리지 않기 무릎밴드 올리고 3초 유지

bookmark_border위대해지는 순간

위대해지는 순간

그 끝없이 밀려드는 두려움
두려울수록 움틀거리는 내 안의 뜨거움
그리고, 엔진의 매스꺼움

나는 이순신을 꿈꾼다


그때 나는 아주 보잘것 없는 빡빡이 이병이었고, 모든게 겁이 나던 어린아이였습니다.

거대한 자연을 만났을때, 이 보잘것 없는 인간이 그것을 마주하고 섰을때, 그리고 내가 그것을 극복하고 있다는것을 느낄때 지금도 생생히 기억나는 마음속의 외침이 있었습니다.

‘덤벼라’

그 순간이 내 인생에서 내가 가장 크고, 위대했던 순간이었던것 같습니다.

bookmark_border여우 길들이기

“난 너와 함께 놀 수 없어.”
여우가 말했다.

“나는 길들여져 있지 않으니까.”

“그럼 어떻게 해야 하는 거지?”
어린 왕자가 물었다.

​”참을성이 있어야 해.”
여우가 대답했다.

“우선 내게서 좀 떨어져서 이렇게 풀숲에 앉아 있어. 난 너를 곁눈질해 볼 거야. 넌 아무 말도 하지 말아. 말은 오해의 근원이지. 날마다 넌 조금씩 더 가까이 다가앉을 수 있게 될 거야……”

다음날 다시 어린 왕자는 그리로 갔다.

“언제나 같은 시각에 오는 게 더 좋을 거야.”
여우가 말했다.

“이를테면, 네가 오후 네 시에 온다면 난 세 시부터 행복해지기 시작할거야. 시간이 갈수록 난 점점 더 행복해지겠지. 네 시에는 흥분해서 안절부절못할 거야. 그래서 행복이 얼마나 값진 것인가 알게 되겠지! 아무 때나 오면 몇 시에 마음을 곱게 단장을 해야 하는지 모르잖아. 의식(儀式)이 필요하거든.”

“의식이 뭐야?”
어린 왕자가 물었다.

여우가 말했다.
“그건 어느 하루를 다른 날들과 다르게 만들고, 어느 한 시간을 다른 시간들과 다르게 만드는 거지. 예를 들면 내가 아는 사냥꾼들에게도 의식이 있어. 그들은 목요일이면 마을의 처녀들과 춤을 추지. 그래서 목요일은 신나는 날이지! 난 포도밭까지 산보를 가고. 사냥꾼들이 아무 때나 춤을 추면, 하루하루가 모두 똑같이 되어 버리잖아. 그럼 난 하루도 휴가가 없게 될 거고……”

여우가 말했다.
“하지만 넌 그것을 잊으면 안 돼. 너는 네 장미에 대해 책임이 있어…..”

“나는 장미에 대해 책임이 있어……”
어린 왕자는 잊지 않도록 되풀이해서 말했다.


문득 묻고는 합니다.
“성실하게 보낸 오늘 하루에 여전히 서운함이 남는 이유는 무엇이냐?”

​그것은 오늘 나의 하루 속에 내가 길들이고 있는 여우가 없기 때문입니다. 매일 매일 같은 시간에 만나 길들이고 있는 나만의 여우가 있었으면 합니다.

bookmark_border세가지 선물

세가지 선물 – 박노해

나에게 선물하고 싶은 것은
단 세가지

풀무로 달궈 만든 단순한 호미 하나
두 발에 꼭 맞는 단단한 신발 하나
편안하고 오래된 단아한 의자 하나

나는 그 호미로 내가 먹을 걸 일구리라
그 신발을 신고 발목이 시리도록 길을 걷고
그 의자에 앉아 차를 마시고 저녁 노을을 보고
때로 멀리서 찾아오는 벗들과 담소하며
더 많은 시간을 침묵하며 미소 지으리라

그리하여 상처 많은 내 인생에
단 한 마디를 선물하리니
이만하면 넉넉하다

bookmark_border지식은 절대적인가, 상대적인가?

지식은 절대적인가, 상대적인가?

과거와 현재의 끊임없는 대화 : E. H. 카 : 역사란 무엇인가?(feat. 라쇼몽)

  • 역사를 바라보는 두 가지 관점
    • 절대주의적 역사관 : 과거에 있는 사실을 있는 그대로 객과적으로 기술
    • 상대주의적 역사관 : 과거에 일어난 사실을 기술할 때 자신의 주관이 들어갈 수밖에 없다
  • E.H.카의 ‘역사란 무엇인가’
    • 역사란 과거와 현재의 끊임없는 대화이다.
    • 라쇼몽(일본영화, 1950년)
      • 도적, 사무라이, 사무라이 아내, 나무꾼(목격자) 의 각각 입장에 따른 진술
      • 이 진술 중 하나만 남겨진다면 역사는 그 증언을 토대로 사건을 기록할 것임.

내 이론은 틀릴 수 없어 : 포퍼 : 반증 가능성

  • 과학을 바라보는 입장
    • 절대주의적 과학관
      • 과학적 지식은 합리적이고 객관적
      • 슐리크, 카르납, 포퍼
    • 상대주의적 과학관
      • 과학적 지식도 상대적이고 주관적
      • 헨슨, 쿤, 파이어아벤트

논리실증주의

  • 프레게와 아이슈타인에 의해 논리학, 수학, 물리학이 혁명적으로 발전하자, 이에 자극받아 슐리크, 카르납 등의 논리실증주의자들은 철학에서도 이와 같은 변화가 있어야 한다고 주장
  • 비엔나 서클
  • 경험론의 후예
  • 귀납법
    • 관찰과 경험을 통해 검증 가능한 명제만이 의미가 있고, 검증 불가능한 명제는 의미가 없다.
    • 검증 가능한 과학만이 진짜 학문이고, 검증불가능한 명제(형이상학, 윤리학, 미학, 종교)는 비과학이다.

포퍼의 반증주의

  • 논리실증주의의 두가지 문제점
    • 귀납법의 한계
      • 모든 백조는 하얗다.
    • 구획의 문제
      • 너도 나도 과학. 인문과학, 사회과학, 창조과학, 타로, 침대도 과학.
      • 검증 가능하면 모두 과학인가?
  • 포퍼
    • 오스트리아 출신 과학철학자 및 사회철학자
    • 논리실증주의에 반대
  • 과학이론은 귀납법이 아닌 가설-연역법
    • 귀납법
      • 개별사실 관찰 -> 일반화 -> 과학이론
    • 가설-연역법
      • 가설1 -> 반례1 -> 가설2 -> 반례2 -> 가설3 -> 반증되지 않을때 과학이론
      • 독일 화학자 케쿨레의 벤젠의 구조
  • 검증이 되더라도 과학이 아닐 수 있다.
    • 아들러 심리학
    • 마르크시즘
    • 사이비 과학
  • 틀릴 수 있는 가능성이 있는 이론만이 과학이론이 될 수 있다.
    • 반증 가능성
    • 포퍼의 반증주의

반증주의에 대한 반론

  • 반증할 수 없는 것 천지다
    • 확률에 대한 가설
      • 주사위를 던졌을때 3이 나올 확률은 6분의 1이다. 반증 불가
    • 존재에 대한 가설
      • 외계인이 존재하지 않는다.
    • 반증주의 자체도 반증 가능해야 한다.
  • 뒤엠-콰인 논제
    • 석촌호수 황금색 백조
    • 어떤 가설을 포기해야할지 결정 필요

공통점

  • 관찰과 경험을 통한 과학적 방법론이 친리를 찾는 가장 확실한 방법이라는 점
  • 과학이 모든 학문의 왕좌라는 점

과학, 너마저… : 토머스 쿤 : 패러다임의 전환

  • 과학을 바라보는 두가지 입장
    • 절대주의적 과학관
      • 과학적 지식은 합리적이고 객관적
    • 상대주의적 과학관
      • 과학적 지식도 주관적이고 경험적
  • 토머스 문
    • 1922~1996
    • 과학혁명의 구조
  • 아리스토텔레스는 물질은 불.공기.물.흙의 4원소로 이루어져 있고, 불과 공기의 고향은 하늘이고, 물과 흙의 고향은 땅이어서, 사과가 땅으로 떨어지는 이유는 고향인 땅으로 가려는 것이다.
    • 당시 패러다임은 ‘물질은 자기가 원래 속해 있었던 자리로 돌아가려는 성질이 있다’고 믿음.
  • 과학혁명은 종교의 개종과 같다.
  • 과학혁명이 일어나는 과정
    • 힘과 권위를 가진 나이든 과학자와 젊은 과학자
    • 나이든 과학자들이 죽으면 새로운 패러다임이 힘을 얻음.
  • 과학자들의 반격
    • 과학의 합리성을 완전히 무시
    • 쿤은 한발 물러서서 과학자들이 어느 정도 합리성을 가지고 더 우수한 패러다임을 선택

원치 않는 결과는 거부한다 : 핸슨 : 관찰의 이론 의존성

  • 오리일까, 토끼일까?
  • 관찰의 이론 의존성
    • 관찰 -> 가설 -> 실험 -> 이론
    • 관찰과 실험의 결과가 관찰자의 배경지식에 따라 왜곡될 수 있다.
    • 관찰의 이론 의존성
  • 원하지 않는 관측 결과를 거부한다.
  • 이미 관측한 결과도 왜곡한다.
    • 자동차 사고영상. 50km/h(그냥 사고) vs. 60km/h (사망한 사고)

과연 그게 당연할까? : 푸코 : 말, 지식, 광기

  • 미셸 푸코
    • 프랑스 철학자 (1926~1984)
    • 지식, 권력, 억압에 관한 많은 이론 제시
    • 지식의 문제, 권력의 문제, 종교의 문제
  • 스웨덴 린네의 생물 분류 vs. 중국 청나라의 분류

플라톤의 에피스테메

동굴안 동굴밖
현실세계
감각으로 경험하는 가시계(Visible)
사물을 눈에 비치는 대로
잘못된 인식
이데아 세계
이성으로 인식하는 가지계(Intelligible)
사물을 있는 그대로
참된 인식
독사(Doxa) 에피스테메(Episteme)
  절대적 보편적 진리

푸코의 에피스테메

  • 지식의 고고학
    • 지식도 신석기 시대에는 신석기 시대형 지식이 있었고, 청동기 시대에는 청동기 시대형, 철기 시대에는 철기 시대형 지식이 있으며, 그 지식들 사이에는 불연속적인 단절이 있었다.
    • 또한 유럽과 아시아에서 나오는 유물이 다르듯, 같은 시대라도 지역에 따라 통용되는 지식이 다르다.
  • 푸코의 시대별 구분
    • 16세기에는 ‘유사성’이라는 키워드로 지식을 구성
      • 호두를 먹으면 머리가 좋아진다.
    • 17세기 ‘표상’, 19세기 ‘주체’
    • 플라톤의 에피스테메는 절대적 지식이지만, 푸코에게 절대적 보편적 지식이나 진리는 없다.
    • 푸코의 에피스테메는 특정지역, 특정시기에 지식을 구성하는 키워드 즉 무의식적 인식체계이다.
  • 광기의 역사
    • 광기의 개념이 시대에 따라 달라졌다.
    • 주체, 이성, 합리성의 개념 등도 시대의 산물로 어느 시대나 통용되는 개념이 아니다.
    • 즉, 보편적인 것, 자연스러운 것, 우리가 당연하다고 생각하는 것들이 사실 아니다.
쿤의 패러다임 푸코의 에피스테메

과학(물리학)에 적용

과학, 철학, 문화 등 모든분야에 적용되는 인식 체계
의식적 작용 무의식 속에서 작동 (알아채지 못함)
나름 합리적 기준으로 선택 합리적 기준 없음
  • 권력의 은밀한 지배
    • 「감시와 처벌」
    • 18세기 후반 감금형과 강제노등 등이 도입
    • 죄수들은 불안과 공포를 느끼며 권력의 시선을 내면화하고 스스로 자기검열을 하게 됨
    • “감옥이 학교나 공장, 병원과 비슷하고, 이들이 감옥과 닮았다고 해서 놀라운 일인가.”
    • 우리 자신이 학교에서, 직장에서 일상생활에서조차 권력의 시선을 내면화해서 스스로 자기검열을 하고 있다는 것.

비슷한 동물끼리 묶기

고양이과 동물 vs. 사육가능한 동물

bookmark_borderOpenfire

Openfire 설치

  • 시놀로지 도커에 설치
  • gizmotronic/openfire
  • 볼륨 설정 (파일 : 마운트)
    • /volume1/docker/openfire : var/lib/openfire
    • /volume1/log/openfire : /var/log/openfire
  • 포트설정
    • 5222, 5223, 9090, 9091, 7070, 7443, 7777
9090 : http web administration
9091 : the secured administration respectively
5222 : Client Port
5223 : Client SSL Port
5269 : s2s, Remote servers can exchange packets with this server on port
5229 : for SparkWeb
7070 : HTTP binding Port
7443 : HTTP binding SSL Port
7777 : File transfer proxy
5347 : External Component
  • http://192.168.219.150:9090/
    • 관리자 계정 : admin
  • 사용자 추가 페이지
    • Registration 플러그인 추가
    • Users/Groups > Users > Registration Properties > Enable users to register via a web page at http://xmpp.kr:9090/plugins/registration/sign-up.jsp

bookmark_borderCordova Todo List

Cordova Todo List 프로젝트

mytodolist 프로젝트 생성 및 시험

D:\cordova>
> cordova create mytodolist com.example.mytodolist MyTodoList
> cd mytodolist
> cordova platform add android
> cordova run android

NodeJS 서버

index.js

var todos = [];

클라이언트

mytodolist.js

var todos = [];

$.ajax("http://localhost:3000/todos").done(function(result) {
    console.log(result);

    todos = result;
    for (const todo of todos) {
        $(".contents ul").append(liTemplate(todo.text, todo.checked));
    }
});

$("#addButton").click(function() {
    var text = $("#inputBox").val();
    todos.push({ text: text, checked: false });
    $("#inputBox").val("");
    console.log(todos);

    $(".contents ul").append(liTemplate(text, false));
    saveTodos();
});

function liTemplate(text, checked) {    
    var li = $('<li></li>');

    alert(text);
    li.attr('value', text);
    li.append(inputTemplate(text, checked));
    li.append(text);
    li.append(buttonTemplate());

    li.click(function(event) {
        var el = $(event.target);
        var index = el.index("li");
        console.log(index);
    
        if (el.is("button")) {
            todos.splice(index, 1);
            $("li")[index].remove();
        }
        else if (el.is("input[type='checkbox']")) {
            var isChecked = el.is(":checked");
            if (isChecked) {
                $($("li")[index]).addClass("checked");
                todos[index].checked = true;
            } else {
                $($("li")[index]).removeClass("checked");
                todos[index].checked = false;
            }
        }

        saveTodos();
    });    

    return li;
}

function inputTemplate(text, checked) {
    var inputTag = $('<input type="checkbox" id="checkBox">');
    inputTag.attr('checked', checked);
    return inputTag;
}

function buttonTemplate(text) {
    var buttonTag = $('<button id="delButton">X</button>');
    return buttonTag;
}

빌드 및 시험

> npm run start

npm ERR! Missing script: "start"
==>
package.json
"scripts": {
    "start": "node index.js"
  },

Error: Cannot find module 'express'
==>
> npm install -save express

> cordova run android

플러그인 설치

날짜선택 플러그인

알림 플러그인

function onSuccess(date) {
    var text = $("#inputBox").val();
    todos.push({ text: text, checked: false, date: date });
    $("#inputBox").val("");
    console.log(todos);

    $(".contents ul").append(liTemplate(text, false, date));
    saveTodos();
}

$("#addButton").click(function() {
    datePicker.show(options, onSuccess, onError);
});

bookmark_borderCordova

Cordova

  • 오픈소스 하이브리드 앱 개발 도구
  • 웹 기술을 이용해 개발
  • 윈도우, OS X, 리눅스에서 사용 가능
  • 안드로이드, iOS, Mac OS X, 윈도우 등 멀티 플랫폼 지원
  • 플러그인을 통한 확장
  • Cordova 의 한계
    • 네이티브 앱에 비해 성능이 떨어짐
    • 플러그인 개발의 어려움

Cordova의 작동원리

Cordova 개발환경

  • NodeJS / Cordova
  • 플랫폼 별 요구사항
    • 안드로이드: JDK, 안드로이드 SDK
  • JDK 다운로드 및 설치
  • 안드로이드 스튜디오 설치
  • Cordova 설치
    • > npm install –g cordova
    • > cordova –version
  • 새 Cordova 프로젝트 생성
    • > cordova create hello com.example.hello HelloWord
    • > cd hello
  • 안드로이드 에뮬레이터 생성
    • > More Actions > Virtual Device Manager > Create virtual device > Pixcel2 > download >
    • > cordova platform add android@8.0.0
    • > cordova run android
      • Android SDK의 경로를 찾지 못하는 문제
        • ANDROID_SDK_ROOT=undefined (recommended setting)
        • > More Actions > SDK Manager > Android SDK Location 확인
        • set ANDROID_SDK_ROOT=C:\Users\user\AppData\Local\Android\Sdk
      • Gradle 을 찾지 못하는 문제
      • javac 를 찾지 못하는 문제
        • Failed to run “javac -version”, make sure that you have a JDK version 8 installed.
        • set PATH=%PATH%;C:\SDK\jdk1.8.0_321\bin;
    • Android Emulator가 띄워져 있는 상태에서 실행
    • 영구적 PATH 등록 : 설정 > 정보 > 고급 시스템 설정 > 환경 변수 > 시스템 변수 > path 에 추가
      • C:\SDK\gradle-7.2\bin
      • C:\SDK\jdk1.8.0_321\bin

코도바 플러그인 설치 및 사용

Cordova 플러그인 설치

Cordova 플러그인

  • NodeJS 의 NPM 과 같이 Cordova 의 기능을 확장
  • 웹 앱에서 OS 기능을 사용하려면 플러그인이 필요
  • OS 종속 코드와 자바스크립트 인터페이스로 이루어짐

Cordova 플러그인 설치 및 삭제

Cordova 배터리 상태 플러그인

  • 디바이스의 배터리 잔량 확인
    • batterystatus 이벤트 -> status.level
  • 디바이스의 충전상태 확인
    • batterystatus 이벤트 -> status.isPlugged
  • 디바이스의 배터리 경고 확인
    • batterylow 이벤트
    • batterycritical 이벤트
  • 플러그인 설치
    > cordova plugin add –save cordova-plugin-battery-status

index.html

    <body>
        <div>
            <span id="battery">100</span>%
        </div>
        <script src="cordova.js"></script>
        <script src="js/jquery-3.4.1.min.js"></script>
        <script src="js/index.js"></script>
    </body>

deviceready 이벤트

  • Cordova 플러그인이 모두 로드 된 후 발생
  • window.addEventListener 함수를 통해 등록
  • 플러그인은 deviceready 이벤트 발생 이후 사용하여야 함

index.js

var battery = $("#battery");

window.addEventListener('deviceready', function() {
    window.addEventListener('batterystatus', function(status) {
        battery.text(status.level);
    }, true);
}, true);
  • Virtual Device Manager > AVD 실행
  • > cordova run android

Cordova 카메라 플러그인

  • 디바이스의 카메라로 사진을 찍어 불러오기
  • 디바이스의 이미지 라이브러리에서 사진 불러오기
    • getPicture 함수
      camera.getPicture(success, error, options)
      • success : 사진을 불러오는데 성공했을 때 실행되는 콜백함수
      • error : 사진 불러오기에 실패했을 때 실행되는 콜백함수
      • options : 사진을 가져오기 위한 option 객체
  • 카메라 플러그인 설치
    > cordova plugin add –save cordova-plugin-camera
  • 설치 플러그인 확인
    > cordova plugin list
        <button id="btnTakePic">사진 찍기</button>
        <button id="btnLoadPic">사진 불러오기</button>
        <div>
            <img id="image"/>
        </div>

index.js

var battery = $("#battery");
var image = $("#image");

options = {
    quality: 80,
    correctOrientation: true
}

window.addEventListener('deviceready', function() {
    window.addEventListener('batterystatus', function(status) {
        battery.text(status.level);
    }, true);

    $("#btnTakePic").click(load(1));
    $("#btnLoadPic").click(load(2));
}, true);

function load(sourceNum) {
    return function() {
        navigator.camera.getPicture(
            function(imageUri) {
                image.attr('src', imageUri);
            },
            function(error) {
                alert('에러발생!');
            },
            {
                sourceType: sourceNum,
                ...options,
            }
        );
    }
}

펼침연산자

            {
                sourceType: sourceNum,
                ...options,
            }
아래와 동일
            {
                sourceType: sourceNum,
                quality: 80,
                correctOrientation: true
            }
  • 배열이나 객체를 피연산자로 받음
  • 피연산자로 전달된 배열이나 객체를 펼침
  • 다른 배열이나 객체에 값을 복사해 추가할 경우 사용
var arr = [1, 2, 3];
console.log([...arr, 4, 5]);

var obj = { a:0, b:2 }
console.log({...obj, c:5});

bookmark_border동시통역 채팅 프로그램 만들기

네이버 파파고 번역 API

Papago NMT API

네이버 API 이용신청

Request 모듈

  • NodeJS 앱에서 HTTP 요청 기능
    • Jquery 의 .ajax 함수와 비슷
  • my_littlechat> npm install –save request
# request 사용법

var request = require("request");

request.get('/books', function(error, response, body) {
    
});

request.post('/books', { body: { key: value } }, function(error, response, body) {
    
});

request.put('/books', { body: { key: value } }, function(error, response, body) {
    
});

request.delete('/books', function(error, response, body) {
    
});
app.post('/send', function(req, res) {
    var message = {
        sender: req.body.sender,
        ko: req.body.ko,
        en: req.body.en
    };
    console.log(message);

    messages.push(message);
    res.status(200).send({ message: "Success" });
});

서버 구현

app.post('/send', function(req, res) {
    var message = {
        sender: req.body.sender,
        ko: req.body.ko,
        en: req.body.en
    };
    console.log(message);

    var options = {
        url: "https://openapi.naver.com/v1/papago/n2mt",
        form: {
            source: message.ko.length == 0 ? "en" : "ko",
            target: message.ko.length == 0 ? "ko" : "en",
            text: message.ko.length == 0 ? message.en : message.ko
        },
        headers: {
            "X-Naver-Client-Id": "ID",
            "X-Naver-Client-Secret": "KEY",
            "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8"
        }
    };

    request.post(options, function(error, response) {
        var result = JSON.parse(response.body).message.result;
        message.ko = message.ko.length == 0 ? result.translatedText : message.ko;
        message.en = message.en.length == 0 ? result.translatedText : message.en;

        console.log(message);
        messages.push(message);
        res.status(200).send({ message: "Success" });
    });    
});

app.get("/receive", function(req, res) {
    var result = { total: messages.length, messages: [] };

    if (messages.length > req.query.from) {
        result.messages = messages.slice(req.query.from);
    }

    res.status(200).send(result);
});

클라이언트 구현

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>my little chat</title>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
    <style>
      .messages {
        width: 500px;
        height: 500px;
        overflow: scroll;
        background: #eeeeee;
      }
    </style>
  </head>
  <body>
    <div class="messages" id="messages"></div>
    <div>
      <select id="selectBox">
        <option value="ko">한국어</option>
        <option value="en">영어</option>
      </select>
      아이디: <input type="text" id="senderId" />
      내용: <input type="text" id="chatInput" />
      <button id="sendButton">전송</button>
    </div>
    <script src="./chat.js"></script>
  </body>
</html>
var curIndex = 0;
var language = "ko";
var messages = [];

setInterval(function() {
    $.ajax(`http://localhost:3000/receive?from=${curIndex}`).done(function(data) {
        for (const message of data.messages) {
            console.log(message);

            $("#messages").append(
                message.sender + " : " + (language == "ko" ? message.ko : message.en)
            );
            $("#messages").append("<br/>")
        }

        curIndex = data.total;
    });
}, 1000);

$("#sendButton").click(function() {
    var message = {
        ko: "",
        en: "",
        sender: $("#senderId").val()
    }

    if (language == "ko") message.ko = $("#chatInput").val();
    else message.en = $("#chatInput").val();

    console.log(message);

    $.ajax({
        url: "http://localhost:3000/send",
        method: "POST",
        data: JSON.stringify(message),
        dataType: "json",
        contentType: "application/json"
    }).done(function() {
        console.log("POST done");
        $("#chatInput").val("");
    });
});

$("#selectBox").change(function() {
    language = $("#selectBox").val();
    console.log(language);
    $("messages").html("");
    curIndex = 0;
});