728x90

이지스 퍼블리싱 서평 이벤트로 책을 무료로 받아 읽고, 서평을 남깁니다.

책을 보았을 때 첫 인상

 책이 500페이지정도, 책의 크기는 A4정도 된다. 초판은 21년 2월이고 약 7개월 뒤인 9월에 개정판이 나왔다. 가격은 3만원이며, 이북으로 구매 시 2만원에 구매 가능하다. 가격에선 다른 안드로이드 프로그래밍 책들과 비슷한 가격을 가진다. 안드로이드 앱 프로그래밍 (자바) 판을 구매한 적이 있는데 이 책보다 더 비싸서 그 책에 비해 저렴하다는 느낌이 쎄다.

1장

여타 다른 책들과 동일하게 초보자를 위한 큼직한 그림의 개발 환경 구축, 설치시 오류 해결에 대한 설명 등을 해준다. 오류 해결에 조금만 더 힘을 써줬으면 좋았을 것 같다. 대개 윈도우를 사용하고 사용자 이름을 한글로 하기 때문에(LAN 꼽고 윈도우 설치시), 안드로이드 스튜디오 오류 범벅에 시달리게 되는데 이를 어떤 이유인지 전문가가 설명해주었다면 좋지 않을까 싶다. 왜냐면 대학 학부 수준에 안드로이드 스튜디오를 설치하면서 어렵다고 느낀 친구들이 많았다. 나 또한 작년 모바일 프로그래밍 수업을 들으면서 사용자명을 한글로 설정하여 여러가지로 시달렸던 것을 생각하면... 그렇다.

 개발환경뿐만 아니라, 플러터에 대한 스키아 엔진, 프레임워크, 엔진, 임베더 등 설명을 해주어서 좋았다. 개발하다보면 어떤 오류를 만났을 때, 왜 그런지 알 때 내가 작업하고 있는 프레임워크가 어떻게 구성되어있는지 아는 것이 중요하다고 생각하기 때문이다.

2장

 앱을 만들다 보면, 서버와 통신을 하다보면 비동기 처리를 해야하는 일이 흔하다. 2장부터 간단하게 다트에 대한 문법, 매우 필요한 비동기 처리를 다루고 있다. 중요한 것을 앞서 다룬 점에서 너무 좋은 것 같다. 추가적으로 스트림에 대해서도 알려준다. 😀  아쉬운 것은 "하나의 스레드로 동작한다"에서 자바와 코틀린과 비교하여 설명해주는 추가적인 페이지가 있었다면 더 좋지 않았을까 싶다. 책과 별개이지만 다트 문법이 꽤나 자바 스크립트와 닮아있다.

3장

 안드로이드와 레이아웃을 정의하는 것이 많이 다른데, 이에 대한 설명이 없다. 내가 못 읽은 것일까..

6장

 5장에서 실습한 내용을 IOS 환경으로 다루기 위해, 실습이 있다. 

7장

 대부분의 사람들이 쉽게 앱을 만들어 수익을 창출하기 위함이라고 생각한다. 그런 면에서 사용해야하는 라이브러리에 대한 라이선스에 대해 언급하는 부분이 인상적이었다. 거의 대부분의 서비스를 만들기 위해서 카카오 API와 같이 네이버, 구글 API를 이용하여 SNS 로그인을 구현해야한다. 이런 부분에서 아이오닉과 같은 JS 기반 크로스 플랫폼의 경우 정보가 적어 찾기도 힘들고, 이런 내용을 책에서 다루는 것을 처음 본다. 이 책을 사는 사람들의 니즈를 충족해주는 것 같다. 

8장

 안드로이드의 경우 인텐드란 개념으로 페이지를 왔다 갔다 한다. 플러터에서는 네비게이터를 이용하여 한다. 탭바보다 이 내용이 앞에 있어야 하는 것이 아닌가란 생각이 들었지만, 플러터의 방식이 JS 프레임워크와 비슷하여 JS 프론트 개발자들은 쉽게 개발할 수 있을 것 같다는 생각이 들었다.

9장-10장

 공유 환경설정, 파일, DB 등 데이터를 저장하는 방법을 여러가지 소개시켜 준다. 안드로이드하면 Room이 떠오르는데, IOS 환경도 구동하려면 사용할 수 없나보다.(추측)

11장

 웹이든 앱이든 애니메이션을 다루는 데 있어, 잼병이었는데, 읽어보니 꽤나 쉽게 다가왔다.

12장

 안드로이드, Ios 네이티브 API 통신 방법도 있다는 것 톺아보기 수준이라 살짝 아쉽다. 

13장

 제일 기대했던 부분이다. 파이어베이스를 이용하여 간편하게 프로그램을 개발할 수 있어요!란 말을 많이 들었고 시도도 해봤지만, 익숙하지 않은 환경에 매번 어렵게 어렵게 했었다.

 요즘 플러터가 인기가 많아지고 있다. 안드로이드, ios 모두 개발하기 위해선 많은 시간이 들어가기 때문에, 구글이 내놓은 해결책인 다트와 플러터를 이용하여 개발한다면 많은 면에서 시간이 절약이 되기 때문일 것이다. 이전 아이오닉, 네이티브 js를 이용하여 크로스 플랫폼을 만들려고 했지만, 여러가지 이유 때문에 계속 별 것아닌 오류 때문에 발목을 붙잡히고 결국 프로젝트를 뒤엎고 PWA로 만들고 있다. 분명 이러한 이유 때문에 JS와 비슷하고, 크로스 플랫폼도 잘 되고, 인기도 많은 플랫폼이 있어야했고, 이 플러터가 그러한 것 같다. 현재 완벽한 해결 방안이라고 말하기 어렵기도 할 것이라 생각한다. 플러터에 관심이 있거나, 크로스 플랫폼을 개발하기 위한다면 이 책을 읽고 공부하면 조금이나마 수월할 것 같다는 생각이 들었다. 먼저 다른 책과 달리 개발시 진짜 필요한 것들, 그리고 수익 창출까지 도모해주는 책은 처음 봤다. to do list 소개하는 책은 많이 봤었다... 하지만 이 책이 만능의 책이냐고 물어본다면 그것은 아니다. 왜냐면 책 앞부분에서도 저자가 말했듯이 이 책의 대상은 프로그래밍 언어를 하나 배운 정도의 수준이기 때문이다.

 분명 플러터로 개발하다보면 여러가지 이유가 생길 것이다. 악담이 아니라 사실이다. 그 때에는 추가적인 자료 조사로 해결해야겠지만. 이 책만큼 실 사용에 관해 말한 책은 처음이다. 말이 계속 반복되니 강조가 아니라 약팔이가 되는 것 같다. 그렇다고 깊은 수준의 지식이 없다는 것만 문제일까, 이 책의 폰트도 조금 크고 (아마 대상자가 초보자이기 때문에), 클론 코딩마냥 코드가 나란히 붙어있다. 나는 이렇게 코드를 나란히 붙여넣고 설명하는 책을 싫어한다. (특히 코드가 한 눈에 안 보이고, 코드 길이가 너무 길어 다음 장에 짤려 나오는 것들) 이론적인 설명만 하고 따로 해설 영상이 있는 것이 더 코딩에 도움이 된다고 생각하기 때문이다. 내가 생각하는 단점은 앞서 말한 2가지다. 그럼에도 앞서 말한 독자의 니즈을 잘 파악한 책이라 Ebook으로 구매해서 볼만한 책이라 생각한다.

 내가 플러터 초중급자, 안드로이드 개발을 조금해본 개발자라 생각한다면 살 이유는 없다. 정말 초급자를 위한, 요즘 부가적인 수익을 위한 분들을 위한 딱 그런 책이라고 생각한다. 저자 또한 이를 초점으로 책을 쓰신 것 같다. 이분이 조금 딥한 플러터 책을 쓴다면 어떤 내용일지도 조금 궁금해진다.

여담) 안드로이드 스튜디오를 다뤄 앱을 만든다는 과정 자체가 대학교 1학년 때 너무 어려웠다. 그렇기 때문에 여러 책을 사서 읽어보기도 했다. 자바로 된 안드로이드 하나, 코틀린으로 된 안드로이드 하나, 코틀린 관련된 2권 등 뭐가 안되면 책을 사서 보는 습관이 있어 많이 샀었다. 모바일 프로그래밍이란 수업에서 안드로이드를 다뤘을 때, 놀란 경험이 있었다. ListView에 내가 원하는 이미지 하나, 텍스트 하나를 리스트뷰의 아이템 뷰로 넣어주는 행위를 해야 했다. 이 때 처음 어떤 책에서도 안 적혀? 아니 자세히 안 적혀 있는 동적 뷰 할당을 알게 되었다. 이에 대해 알게 된 곳은 공식 문서였다.  이후 이 개념은 리사이클러 뷰에 대한 기본 지식이 되었다. 이 책을 읽고 이제 난 "짱짱 플러터 코딩러다"고 외치면 참 좋겠지만 분명 직접 개발하면 막힌다.(악담같겠지만, 진짜 그렇다.) 꼭 플러터 공식 문서에가서 내용을 한번씩 훑어보거나 막힌 부분에서 막힌 것에 대한 내용을 공식 문서를 훑어보자. 이 때 입문서로 이 책과 같이 읽어나간다면 효율이 좋다고 생각한다.

반응형
728x90

 이전 이집트인의 곱셈에서 이진수를 잘 모르고 있다는 느낌을 받았다. 아마 이진수를 잘 몰라도 코딩은 가능하겠지만, 알면 더 잘할 수 있지 않을까? 아님 남들보다 조금 더 간지나는 코딩이 가능하지 않을까? 이후 이 글자색으로 현실의 수에 비유하여 공감시켜드리겠습니다.

 이진수

  논리회로 책에서 이진수를 아래와 같이 설명한다.

2진수에서는 낮은 전압 레벨(0V)을 0, 높은 전압 레벨(5V)을 1로 각각 대응시킨다.
그와 같이 표현되는 0과 1의 값을 가지는 기본 데이터 단위를 2진 숫자고 하며 간략히 비트라고 부른다.

 우리가 과학 시간에 배운 전구를 키는 회로를 생각해보면, 전구가 켜지면 1, 전구가 꺼지면 0이다. 논리회로를 배웠고, 마이크로 프로세서 수업을 들었음에도 불구하고 전구 하나만 생각했고, 그냥 그게 2진수가 비트다. 비트가 여러개 보여서 바이트고 이런 식으로 생각했다. 단순하게 지식만 익힌 것 같다. 

 지금 생각해보니, 전구가 2개면 우리가 가지는 상태는 4개이다. (전구 = 비트) 전구가 3개면 상태가 8개다. 당연한 이야기를 하고 있다. 10진수에서 100을 10으로 나눠보자. 100/10 = 10이다. 1000을 10으로 나누면 100이다. 패턴이 보인다. 10진수에서 10으로 나눈다는 건 10진수에서 뒤에서 하나 자리수를 뺀 것이다. 예를 123에서 10으로 나누면 12이가 된다. 1000을 10^2으로 나눈다면 뒤에서부터 2자리를 뺀 것이다. 

10진수에서 10^n으로 나눈다는 것은 뒤에서부터 n개 뺀 것(Shift)이다.

 2진수 또한 다를까? 2진수도 동일하다. 8진수도 동일하다. 16진수 또한 동일하다. 2진수에서의 Shift 연산이 2진수이기 때문에 왼쪽으로 움직이면 2배가 되고, 오른쪽으로 가면 2로 나누는 것이 된다. 그리고 이집트인의 곱셈에서 이진수로 41을 표현하였을 때 0010 1001이다. 이집트인의 곱셈에서 비트 1이 존재하는 부분에 따라 a의 2배한 값을 더 했었다. (41은 2^6 + 2^4 + 2^1) 각 숫자의 의미를 제곱의 의미로 가진다고 생각하고, 그 의미를 가진 전구가 켜져있다고 생각하니 왜 41이 저렇게 표현이 되고, a의 2배한 값을 더 한다는 의미를 이해했다. 

2진수에 대해서 알고 있다면 Shift 연산이 왜 2배인지 2로 나누는지 알면서 쓸 수 있지 않을까? 또한 이집트인의 곱셈의 경우 또한 방법을 이용만 하는게 아니라 이해하여 사용할 수 있지 않을까 싶다.

반응형
728x90

이집트 알고리즘은 인류 최초로 기록된 알고리즘 중 하나다. 빠른 곱셈 알고리즘, 빠른 나눗셈 알고리즘이다.

먼저 알아둬야하는 상식은 고대 문명의 알고리즘이기 때문에 자릿수 개념0을 표현하는 방법이 없었다.
자릿수 개념이 없었다는 말이 나중에 나올 이진수와 비슷하다고 생각이 든다.

먼저 곱셈은 1) 1로 곱하기, 2) 1보다 큰 수로 곱하기, 2가지로 정의하여 나눌 수 있다.

1) 1a = a

2) (n+1)a = na+a

 먼저 우리가 알고 있는 일반 상식? "곱셈은 덧셈을 여러번한 것이다"를 구현해보자. 
=> 덧셈을 n-1번 반복해보는 알고리즘(n-1번 반복하니 시간 복잡도는 O(n)라고 알 수 있다.)

우리는 덧셈을 배울 때 결합 법칙을 배웠기 때문에 덧셈의 횟수를 줄일 수 있다.

 중학교 때 배우는 간단한 덧셈의 결합 법칙을 코드에 녹여낸다는 사실 자체가 신비롭다.
다음은 4a = ( (a + a) + a) +a 덧셈을 4번을 한다. 여기서 결합 법칙을 응용한다면, 4a = (a+a) + (a+a), a+a를 한 번하고 a+a끼리 한번 더해주면 총 2번을 한다. 다시 씹어 먹어보자. 4a = (a+a) + (a+a) <= a를 2배로 하고 2배한 만큼 더한다.

우리가 곱셈 시에 a 하나만을 이용하여 곱하는 것은 아님으로 일반적인 식을 n*a라고 했을 때, n을 반으로 줄이고, a를 2배로 키워서 2를 거듭제곱한 횟수만큼 더한 값을 만든다고 할 수 있다. (물론 책에서 이렇게 말한다.)

# 왜 n을 반으로 줄일까?

n = 41, a = 59

1      59
2     118
4     236
8     472
16     944 
32   1888

 이 부분이 제일 어려운 것 같다. 일단 먼저 자릿수 개념이 없던 이집트에서 수를 어떻게 표기하였는지 모르겠다. 또한, 이 알고리즘을 소개한 책에서 이집트에서 어떠한 방식으로 수를 표기하였다고 추가 설명이 없다. 그럼으로 자릿수 개념과 0에 대한 개념이 없는 이집트에서 이진수를 이용하여 표기했다고 가정해보고 41을 2진수로 표기한다.

0010 1001

 위에 비트를 보고 41을 2의 거듭제곱으로 표현했다고 말할 수 있다. 41은 2^6 + 2^4 + 2^1이기 때문이다.
# 책을 보고  이렇게 분석한 것뿐이지 실제로 이런 생각을 스스로 할 수 있을지 걱정이다.

위에서 생각한 결합 법칙을 이용한 알고리즘을 코드로 작성한다면 아래와 같이 할 수 있다.

여기서 덧셈 횟수를 구해보면 #+(n) = ceil(log n) + ( v(n) -1 ) 와 같다. log n의 이유는 재귀함수로 함수가 돌아가며, 한번 돌 때마다 n의 수가 반절씩 줄어들기 때문이고, v(n)은 홀수인 경우의 수를 의미한다. 거기에 n이 1일 때를 빼줌으로 -1을 한다.

알고리즘 개선

이진수로 생각한다는 것은 무엇일까?

이진수의 정의는 0과 1을 이용하여

반응형
728x90

이전 글에서 스택의 고정 관념을 파쇄를 위해서 글을 쓴다고 했다.  이전글  

먼저 백준의 괄호란 문제를 분석해보자!

9012 괄호 문제

괄호 문자열 Parenthesis String은 (, )로만 구성되어있는 문자열이다. 괄호의 모양이 올바르게 구성된 괄호문자열을 VPS라고 부르고, ( )은 기본 VPS이다.. 예를 들어 (())))() ((())), (() 은 VPS가 아니다.

 선입후출! 먼저 넣고 빼면서 생각하는 방법으로 코딩을 한다면 어떻게 될까? 일단 위 문자열 사진처럼 읽으면서, 스택에 다 넣어두고 뒤에서부터 빼면서 쓴다면 내가 데이터를 읽는 흐름이 보라색 선과 같다.

 이렇게 넣은 상태에서 넣은 값을 하나씩 빼면서 생각한다면 어떻게 생각하게 될까? 어떻게 코드를 짜야할지 감이 잡히지 않는다. 왜???? 왜냐면 넣고 빼면서 읽는 것은 단순하게 뒤에서부터 읽는 것과 마찬가지이기 때문이다.

흔한 고정 관념에 따른 생각 

나는 스택 문제를 풀고 있어!, 그래서 스택을 이용해야해(고정관념에 묶인 중), 그래서 스택에 담긴 값을 뺀 것을 다시 넣어주기 위해서 스택을 하나 더 사용해야겠어!!란 생각을 할 수 있다. 이런 경험이 있다면, 고정관념에 묶인 노예란 증거..


스택: 거꾸로 읽기 가능!

스택 없이 거꾸로 읽고 싶다면, 배열과 for문을 이용하여 아래와 같이 코드를 작성해야한다.

스택을 이용한다면?

이로써 스택에 넣고 빼면서 읽는 행위는 거꾸로 읽는 행위란 것을 알 수 있다.

이제 데이터를 원래 흐름대로 읽어보자.


우리는 앞에서부터 읽으면서 ( 일 때 스택에 넣고, )일 때 스택에서 뺀다. 모든 데이터를 다 읽은 뒤에 스택에 아무것도 없다면 올바른 괄호 문자열일 것이다.

만약에 )일 때 스택에 (이 없거나, 스택에 (이 남아 있거다면 올바르지 않은 문자열인 것을 알 수 있다.

상대적으로 처음에 배우는 기초 자료구조인 스택은 특별하지 않다. 하지만 익숙하지 않기 때문에 자유자재로 이용하지 못할 뿐! 다음 글에서는 10799 쇠막대기란 문제로 스택을 응용해보려고 한다.

반응형

'코딩 관련 > c++' 카테고리의 다른 글

백준 1, 2, 3 더하기 5 문제 분석  (0) 2022.08.09
이집트인의 곱셈  (0) 2021.08.24
자료구조 Stack 구현하기  (0) 2021.08.23
백준 2581 소수 코드 포함  (0) 2020.10.10
백준 하노이의 탑 분석 설명  (0) 2020.08.06
728x90

"알고리즘 공부를 하면서 자료구조에 대해서 공부를 해야한다"란 말을 많이 듣는다. 자료구조 라이브러리는 이미 구현되어있는데, 실제 구현 방법에 대해 알고 있는 것이 좋다고 한다. 이유로는 어떻게 돌아가는지를 알기 때문에 실제 구현 시에 도움이 된다고 하는데, 조금 기분이 이상하다.

 우리는 이미 배열이란 자료구조를 애용한다.

 "배열 없이 못 살아"란 말을 할 정도로 배열 자료구조를 많이 사용한다. 조금 코딩하다보면 배열을 찾게 된다. 이처럼 다른 자료구조들도 친근해야하는 것이 중요한 게 아닐까? 친근하다의 기준이 무엇일까? 많이 이용해봤기 때문에 그 자료구조를 적재적소로 이용할 수 있다는 뜻일 것이다. -> 적재적소로 이용하기 위해선 무엇이 필요할까? 어떻게 구현되어있는지 알고 있어야 가능하다.

스택의 개념

스택은 유명한 Stack이란 게임과 같다.

아님 롤에서 나오는 나서스 스택과 같다.

여기서 문제가 생긴다. 스택은 단순히 쌓는 친구야! 맞아! 그리고 처음 쌓은 것은 마지막에 나오지! 바로 선입후출, 후입선출이야!!

그리고.. 내 고정 관념은 선입후출로 굳어졌다.

무조건 쌓는다!란 생각때문에 일단 스택에 데이터를 넣고 본다. 그러니까 스택을 쓴다는 것은 데이터를 넣고 본다. 그리고 빼내면서 사용한다. 선입선출 말 덕분에 방법이 하나로 굳어진다. 넣을 때 연산을 할 수 있지 않을까란? 생각을 해보자. 추후 이어지는 글에서 자세하게 다뤄볼 생각이다.

스택 구현 방법론

스택뿐만 아니라 자료구조를 구현할 때 여러가지 생각이 든다. 1. 내 실력이 좋지 않다. 2. 정해진 방법이 없다.이다. 코딩 시에 정해진 방법이 없으면 더 힘들어진다. 맞는 방법이 없으니까!! 물론 나도 정답을 가지고 있지 않지만, 현재 실력에서의 방법을 정해서 정리해보려고 한다.

1. 정적 배열 이용

 C++은 기억이 가물 가물하지만, 일단 시도한 바에 따르면 배열 길이의 초기화를 배열 선언시 해줘야한다.
Java는 배열을 선언하고 나중에 배열의 길이를 초기화가 가능하다.(와.. 부럽다)

컴파일 에러가 없으니 된다고 본다.

Java로 한번 짜봐야 겠다.

타입이 여러가지로 사용이 가능하니, 배열의 이름을 지어주는 것이 제일 애매하다.

C++로 짠다면 아래와 같이 작성한다.

2.  동적 배열(포인터) 이용

 비주얼 스튜디오에서 위에 올린 클래스랑 이름이 동일하다고 에러 발생!을 알려줘서 이름을 Dstack이라고 바꿨다.
코드를 보면 알겠지만, 동적 생성 부분만 다르지 거의 비슷하다.

정적과 동적의 차이

 정적은 이미 내 스택메모리에 메모리를 쌓는 행위이기 때문에 빠르다. 동적은 내 힙 메모리에 쌓기 때문에 느리다. 실제 알고리즘 문제를 풀 때에 정적을 사용할까? 동적을 사용할까? 의문이다. 
 만약에 회사 면접에 가서 알고리즘 문제를 풀 때 스택을 구현해야한다면 정적으로 구현해야할까? 동적으로 구현해야할까? 모르겠다. 아직 레벨이 부족하기 때문에 모르는 것일까? 


어떤 언어로 구현하는지는 중요하지 않다. 이 자료구조를 배열처럼 익숙해지려면 직접 문제에 적용을 해봐야하니 다음 글에서 문제에서 스택을 어떠한 방식으로 이용하였는지 분석해보자!

반응형

'코딩 관련 > c++' 카테고리의 다른 글

이집트인의 곱셈  (0) 2021.08.24
스택 응용 분석해보기 1편  (0) 2021.08.24
백준 2581 소수 코드 포함  (0) 2020.10.10
백준 하노이의 탑 분석 설명  (0) 2020.08.06
백준 1011 Fly me to the Alpha Centauri 분석  (0) 2020.07.22
728x90

로컬 로그인을 구현하지 않고, Oauth2.0을 통해 간편한 로그인만 구현하고 싶다는 생각이 들었다.
인터넷에는 Oauth2.0이 훨씬 쉽고, 보안 측면에서도 좋다고 한다. 막상 하다 보면 어렵다.

이 글은 지난 1달 동안 Oauth2.0에 관련하여 삽질한 것에 대한 이야기를 담은 것이며, 제 경험을 공유하여 Oauth2.0 입문자들이 조금 더 쉽게 이해하길 원하며 적고 있다. 물론 누군가에게 검증받은 글이 아님으로 모든 것이 맞다고는 볼 수 없다.

먼저 JWT가 무엇인지 알아보고, JWT를 이용하여 지칭한 AccessToken과 refreshToken을 알아본다. 그 후 직접적으로 사용하는 JWT 이용을 알아본다. 그 후 우리가 진정으로 하고 싶은 SNS에서 제공해주는 Oauth2.0에 대해 알아본다.

1. JWT?

Oauth2.0을 사용하기 위해서는 Jwt란 것을 알아야한다. jwt는 Json Web Token이다. https://jwt.io/에 가면 payload에 값을 담아서 jwt를 생성해보는 실습을 할 수 있지만, 이런 개념은 카카오, 네이버, 구글을 사용 시엔 덜 중요하다. 만약 내가카카오, 네이버, 구글과 같이 직접 jwt를 발행해준다면 알아야하는 내용이지만, 우리는 3사에서 직접 jwt를 발급해주니 3사가 넣은 내용이 토큰에 담겨 있겠구나 정도만 알아두자.

 

JWT.IO

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

jwt.io

더보기

하지만 궁금한 사람도 있을 수 있으니 간단하게 알아보자.

먼저 JWT에 들어가면 아래와 같이 HEADER와 PAYLOAD, VERIFY SIGNATURE가 있다는 것을 알 수 있다.

헤더에 들어가는 alg는 우리가 사용하는 alg를 말하고 있고, 페이로드에는 우리가 담고 싶은 값을 담아 두면 된다.인증 시그니쳐는 우리가 사용하는 비밀 키가 들어간다.
즉, 우리가 설정한 Payload와 함께 헤더에 지정된 alg, 그리고 지정한 비밀키와 함께 아래와 같은 토큰이 생성된다.

우리가 카카오와 네이버와 같은 곳에 받는 jwt 토큰은 헤더, 페이로드, 인증 시그니쳐로 이루어져있고, payload에는 각 회사가 설정한 내용, 비밀키가 지정되어있다는 것을 알 수 있다.

2. access_token과 refresh_token

jwt는 2가지의 token이 있는데 이는 맞는 말이며 틀린 말일 수도 있다. 왜 그런지 알아보자. jwt.io에 들어가면 jwt하나만 보여준다. 어라? access_token과 refresh_token으로 2가지 있는 것을 알고 있는데 jwt2.0엔 하나만 보여주고 있다. 사실 access_token과 refresh_token은 jwt이다. jwt를 풀어쓰면 json web token이니 jwt 자체가 토큰이란 것이다.

2가지 토큰이 있는 이유는, 사람이 jwt의 생존 기간을 수정하여, 생존 기간이 짧은 것은 access_token, 생존 기간이 긴 것은 refresh_token이라고 지칭한 것 뿐이다. 물론 jwt를 암호화에 쓰이는 시크릿 키가 다르게 들어가겠지만, 결국 사람이 이름을 지어준 것 뿐이다. 앞에 생존 기간이란 말이 나왔는데, 생존 기간이란 함은 토큰이 생존할 수 있는 기간으로 만약 2분의 생존 기간을 설정한 jw token이 있다고 하면 2분 뒤 이 token은 사용할 수 없게 된다. 뇌피셜이지만, 아마 검증 시 생존 기간을 검사하여 가려내는 것 같다.

jwt에 들어가는 시크릿 키

우리는 이제 access_token과 refresh_token이 시크릿 키가 다르고(같을 수도 있다.), 생존 기간이 다른 jwt라는 것을 알게 되었다. access_token은 사용자 인증에 쓰이고, 그래서 생존 기간은 refresh_token보다 짧다. refresh_token은 access_token이 만료되었을 때 access_token을 발급 시에 인증 과정에서 쓰인다.

3. 직접 JWT를 사용하는 방법은 무엇일까?

jwt를 발급하기 위해서는 서버에서 발급을 해줘야 할 것이다. 여러 언어에 라이브러리가 존재할 것이고 이를 이용하면 jwt를 만들 수 있다. express를 사용하여 jwt를 만드는 것을 해보았는데, 라이브러리 덕에 한 줄로 가능하며, 의외로 쉽다.
왜 이런 소제목을 만들어 사용하는 방법을 적는지 모르겠지만, 나중에 내용을 추가하거나 지우는 것도 한 방법일 것 같다.;;

4. 그래서 네이버, 카카오, 구글은 어떻게 사용하는 거야?

사용하는 방법을 알아보기 전에 전체적인 흐름을 알아보고, 웹, 모바일 환경에서 어떻게 진행되는지 알아보자.
먼저 왜 다른 환경을 고려하고 있을까? 프론트에서 로그인을 구현할 수도 있고, 백에서도 가능하고 도대체 어디에 해야하는지 모르겠다. 왜 이런 생각이 들었는지 하나부터 끝까지 정리해보겠다.
1. 프론트에서 진행하고 token을 발급받는 code를 백에 보낸다. 그 후 백이 code를 받고 token을 발급한다.
2. 프론트에서 code와 token까지 발급받고 백으로 보낸다.
3. 백에서 code부터 토큰까지 발급받는다.
token 발급 시 밖으로 노출되면 안 되는 것들이 있음(시크릿 키)으로 백에 Token을 발급하는 것이 옳은 것 같다. 그럼 굳이 프론트에서 code를 받아서 백으로 보내는 귀찮음이 있어야할 까? 그냥 백에서 구현하면 안 되나?
확실한 방법은 없는 것 같다. 각 프로젝트 마다 방법이 다 다르고, 처음에 이런 이유 때문에 초보자들은 어려움을 느끼는 것 같다.

4-1. Passport

Express에서 Passport를 이용하여 code와 token을 받는 것이 한번에 이루어진다. 😮 이 때 accessToken과 RefreshToken이 나오는데 대부분 버린다.? 🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔🤔
수많은 검색 중에 카카오에 추가적인 정보 요청을 하는 경우 저장하라고 되어있는데 조회를 안 하는건가?
Jwt 정의에서 accessToken을 가지고 검증한다고 하였는데, 그것을 버린다? 그렇다면 express에서 지원하는 session을 이용해서 사용자 검증을 하는 걸까? token 검증만 하면 굳이 세션을 가지고 있어야 할까? 그렇다면 내가 직접 Jwt 토큰을 발급한다면 어떻게 될까? 그럼 되는거 아닐까?

4-2. Nest Js

Express의 상위 버전?이라고 말할 수 있는 NestJs의 공식 사이트에서는 순수 JWT를 이용한 로그인 구현을 소개하고 있다. < 한글로 번역된 사이트도 있으니 참고할 것>
NestJS 공식 문서를 따라 하다 보면 JWT를 직접 생성하고, 프론트로 보내주고, 백에서 받아 인증하는 것까지 쉽게?... 아니고 할 수 있다. 하다보면 드는 생각이 세션을 유지할 이유가 없다라는 것이다. JWT 자체에 생존 기간이 존재함으로 생존 기간이 만료되면 자동으로 거부된다. 따로 세션을 유지할 필요가 없고, JWT만으로 로그인 검증이 가능하다는 점이 참 좋은 것 같다. 이것이 옳은 것인지는 모른다.

4-3. 다시 Passport

생활코딩에서는 session을 이용하여 인증을 유지한다. 그렇다면 2가지 방법이 존재하는 것이 아닐까?
생활코딩과 같이 세션을 유지하거나, 아니면 우리 어플에 맞게 jwt를 발급하여 검증하거나, 아니면.. accessToken과 refreshToken을 받은 것을 이용한다거나 만약 3번째 방법을 이용한다면 내가 직접 검증 시 이 token이 어느 회사 token인지 몰라 조금 고민이 될 것 같다.

반응형
728x90

재귀 함수를 이용한다는 것은 맹목적 믿음이 필요하다. 이전 글에서 재귀 함수를 완전히 이해했나 싶었지만, 하노이의 탑을 만나고 다시 좌절했다.

 재귀함수는 큰 문제를 작은 문제로 나누어서 작은 문제에만 신경을 쓰면 되기 때문에 재귀는 가독성도 좋고, 코드도 짧아지고, 각 단계의 변수 상태 또한 스택 프레임에 저장되기 때문에 너무나 좋다. 어렵게 느껴지는 하노이의 탑을 현 문장의 장점에 맞춰 생각해보려고 한다.

1. 큰 문제를 작은 문제로 나누기

먼저 우리가 해결해야하는 첫번째 목적(문제)는 제일 하단에 있는 것을 목적지로 옮기는 것이다. 그리고 두번째 목적은 최하단 위로 있는 모든 원반(n-1)개를 목적지가 아닌 다른 곳에 두어야한다.

이렇게 부분 문제가 있다. 

2.  원반이 2개있는 경우

여기서 파란색과 노란색원반을 목적지 C로 옮기고 싶다.

먼저 우리가 해결해야하는 문제는 파란색을 C로 옮겨야한다. 그러기 위해서 노란색 원반을 B로 옮겨야한다.

그러니 이걸 정리하면
1. 파란색원반(제일 마지막 원반)을 C이란 목적지로 옮겨야한다.
2. 노란 색원반( 최하단 원반을 제외한 나머지 원반)을 B로 옮겨야한다.

2를 해결해야지 1을 해결할 수 있게 된다.

3. 원반이 3개이 있는 경우


1. 파란색원반(제일 마지막 원반)을 C이란 목적지로 옮겨야한다.
2. 노란 색원반과 주황색 원반을( 최하단 원반을 제외한 나머지 원반)을 B로 옮겨야한다.

이제 코드를 짜보기 위해 생각해보자.

우리는 두가지 문제를 해결해야한다.

1. 최하단  원반을  출발지에서 목적지로 옮긴다.
2. 최하단 원반을 옮기기 위해서 나머지 원반을 출발지에서 경유지(B, 처음의 경우)로 옮겨야한다.
즉 나머지 원반을 출발지로 부터 목적지(경유지)로 옮겨야한다.

결국 1번과 2번은 같은 꼴이다.
왜냐면 출발지로부터 목적지로 원반을 이동시키면 되기 때문이다.

이를 코드로 작성하면 아래처럼 된다.

#include <iostream>
using namespace std;

void hanoi(int n, int from, int by, int to){
	hanoi(n-1, from, to, by);
}

n-1개인 나머지 원반을 출발지로부터 목적지(경유지)로 일동시켜라. 그 다음 문제는 무엇이었는가?

마지막 원반을 출발지에서 목적지로 이동시키는 것이었다.

#include <iostream>
using namespace std;

void hanoi(int n, int from, int by, int to){
	if( n == 1 ) {
          cout << from << ' ' << to << '\n';
          return ;
    }
}

그리고 나머지 n-1개 원반을 다시 C로 이동시켜야한다.

#include <iostream>
using namespace std;

void hanoi(int n, int from, int by, int to){
	if( n == 1 ) {
          cout << from << ' ' << to << '\n';
          return ;
    } else {
    	hanoi(n-1, from, to, by);
        hanoi(n-1, by, from, to);
    }
}

이렇게 작성한다면 n이 1이 되었을 때 밖에 이동 경로를 찍을 수 밖에 없다.
중간 경로를 찍기 위해서 재귀 호출 두 사이에 경로를 찍어줘야 한다. 이때 생각해야하는 것은 주황색 원반이다. 주황색원반이 제일 먼저 움직이기 때문이다. 주황색 원반은 1(from)에서 3(to)로 이동함을 알 수 있다. 이렇게 생각할 수 있는 이유도 재귀문제는 큰문제를 작은 문제로 나눠기 때문이다. 

#include <iostream>
using namespace std;
void hanoi(int n, int from, int by, int to){
    if(n == 1){
        cout << from << " " << to << "\n";
        return;
    } else{
        hanoi(n-1, from, to, by);
        cout << from << " " << to << "\n";
        hanoi(n-1, by, from, to);
    }
}

 

하노이의 탑을 배우는 이유는 재귀 함수를 어떻게 작성할 수 있는지 배우기 위함이다. 그런데 하노이의 탑에 너무 매몰되어 재귀 학습을 잊어버리면 안된다. 또한 점화식을 제대로 이해하고 작성하면 좋겠지만 수학을 잘하지 못하면 쉽지는 않다. 그러니 점화식을 외워서 이 문제를 해결한다면 다시 만났을 때 풀지 못한다. 그러니 무작정 암기하지 말자!

반응형
728x90

가상 메모리 관리자의 입장에서 비어 있는 메모리가 많을 수록 일은 쉬워진다. 페이지 폴트가 발생하면 빈 페이지 리스트에서 비어 있는 페이지를 찾아서 폴트를 일으킨 페이지에게 할당하면 된다.

만약 빈 메모리 공간이 없다면? 그런 경우 OS는 memory pressure을 해소하기 위해 다른 페이지들을 강제적으로 paging out하여 활발히 사용 중인 페이지들을 위한 공간을 확보한다.
evict(내보낼) page 선택은 os의 replacement policy안에 집약되어 있다.
replacement policy가 중요하다. 그렇다면 ↓

Q. How to decide which page to evict?

1. Cache Management

policy에 대해서 말하기 전에 문제에 대해 좀 더 상세하게 알아보자.
시스템의 전체 페이지들 중 일부분만이 메인 메모리에 유지된다는 것을 가정하면→ 메인 메모리는 시스템의 가상 메모리 페이지를 가져다 놓기 위한 Cache로 생각될 수 있다.
이 Cache를 위한 replacement policy의 goal은 Cache miss의 횟수를 최소화하는 것이다. → 즉, disk로부터 page를 가져오는 횟수를 최소로 만드는 것.
한편으론 Cache Hit 횟수를 최대로하는 것도 Goal이라고 할 수 있다.접근된 페이지가 메모리에 이미 존재하는 횟수를 최대로 하는 것

Cache Hit와 Cache Miss의 횟수를 안다면 프로그램의 average memory access time, AMAT을 계산할 수 있다.(컴퓨터 아키텍트가 하드웨어 캐시의 성능을 측정할 때 사용하는 미터법)

Ex) 어떤 컴퓨터가 4KB의 작은 메모리를 가지고 있고, 페이지의 크기는 256바이트라고 하자.

가상 주소는 4비트 VPN(최상위 비트)과 8비트 offset(최하위 비트)의 부분으로 구성된다. 그러므로 이 예제에서 한 프로세스가 2^4 또는 총 16개의 가상 페이지들에 접근할 수 있다.
프로세스는 가상 주소 0x000, 0x100, 0x200, 0x300, 0x400, 0x500, 0x600, 0x700, 0x800, 0x900의 메모리를 가리키고 있다. 이 가상 주소들은 주소 공간의 첫 10개 페이지들의 첫 번째 바이트를 나타낸다.

가상 페이지 3을 제외한 모든 페이지가 이미 메모리에 있다고 가정하면 주어진 순서의 메모리 참조는 히트 히트 히트 미스 히트 히트 히트 히트 히트 히트된다 . hit reat을 계산하면 90%가 된다.

The Optimal Replacement Policy


Optimal Replacement plicy와 비교하면 Replacement Policy 동작을 잘 이해할 수 있다. 이 policy는 miss를 최소화한다. 간단하지만 구현하기 어려운 정책이다.
좀 더 생각해보자. 만약 페이지 하나를 Evict한다면 지금부터 가장 먼 시점에 필요하게 될 페이지를 버리는 것이 좋지 않을까? 핵심은 가장 먼 시점에 필요한 페이지보다 Cache에 존재하는 다른 페이지들이 더 중요하다는 것이다.

Ex) 프로그램이 0, 1, 2, 0, 1, 3, 0, 3, 1, 2, 1의 순서대로 가상 페이지들을 접근한다고 가정하자.
Cache에 3개의 페이지를 저장할 수 있다고 가정할 때 아래 그림은 최적의 기법의 동작을 보여준다.

  • 말로 설명하기그러면 어떤 페이지를 교체해야할까? optimal replacement policy의 경우 현재 load된 페이지 0,1,2의 미래를 살펴본다. 0은 거의 즉시 접근될 것이고, 1은 약간 후에, 그리고 2는 가장 먼 미래에 접근이 될 것이라는 것을 알 수 있다. 그러므로 optimal policy는 쉬운 선택을 한다. 페이지 2를 Evict한다. 결과적으로 0,1,3이 남게 된다. 그후에 세번의 참조는 Hit가 된다. 그 다음 페이지 2를 만나서 또 한번의 Miss를 만난다. 여기서 한번 각 페이지의 미래를 검사하고 페이지 1만 아니면(바로 다음에 접근 될 예정이라) 어느 페이지를 내보내도 괜찮다는 것을 알게 된다. 이 예제에서는 페이지 3을 내보냈다. 최종적으로 페이지 1을 히트되고 종료된다.
    캐시의 히트율을 계산할 수 있다. 캐시 히트가 6번의 미스가 5번이었으므로 히트율은 6/(6+5)또는 54.5%가 된다. 강제 미스을 제외한 히트율을 계산하면 85.7% 히트율을 얻는다.
    불행하게도 스케줄링 정책을 만들 때도 보았는데 미래는 일반적으로 알 수 없다. → 그렇기 때문에 범용 OS는 Optimal policy의 구현은 불가능하다.
  • Cache는 처음에 비어 있는 상태로 시작하기 때문에 첫 3번의 접근은 Miss다. 이러한 종류의 Miss는 cold-start miss 또는 compulsory miss라고한다. 그 다음에 페이지 0과 1을 참조하면 cahce에서 hit된다. 마지막으로 또 다른 miss를 만나게 된다.
    이 때 Cache가 가득 차 있어서 replacement poliy 를 실행해야한다.

                                      | Scheduling에서처럼 여러가지 Policy가 등장한다. |

A Simple Policy: FIFO

복잡한 구현은 때려치고 매우 간단한 replacement policy을 채용하였다. 예를 들면, 일부 시스템에서는 FIFO replacement policy을 사용하였다. 이 방법은 시스템에 페이지가 들어오면 큐에 삽입되고, 교체를 해야 할 경우 큐의 꼬리에 있는 페이지가 내보내진다. 매우 간단하다 😆

이제 위 이미지의 예제를 살펴보자.
처음에는 마찬가지로 0, 1, 2에 대한 3개의 cold-start miss로 시작한다. 그 이후 0과 1이 다음에 HIT된다. 페이지 3이 참조되지만 Miss를 일으킨다. FIFO를 사용할 때 교체 결정은 쉽다. 순서상 첫 번째로 드러온 0을 선택하면 된다. 그다음 참조 페이지가 0이라 또 Miss가 발생하고 그 다음 페이지 1을 버린다. 간단하지만 성능이 안 좋다 히트율이 36.4%가 된다. 강제 미스를 제외하면 57.1%가 된다. FIFO는 블럭들의 중요도를 판단할 수가 없다.

Another Simple Policy: Random

항상 이런 식이었어 알고리즘.. 항상 잘 안되면 Random을 찾더라...
구현하기 쉽다. 중요도를 판단하지 못하니까 랜덤으로 버리는 것이다. ㅋㅋㅋㅋ 상당히 유쾌 흑쾌 운이 좋으면 한번도 쓸데 없는 Miss가 발생하지 않을 것이니까 운이 안 좋으면 최악이 나타날 것이다.

일반적인 성능을 알아보기 위해 수천번 반복해볼 수 있다. 만번을 수행했을 때 몇번의 Hit를 얻어냈는지를 나타낸다. 어떤 때는 40%가 살짝 넘는 경우들이 optimal 기법만큼 좋은 성능을 보이며 예제에서 6번 Hit를 얻었다. 때로는 안 좋은 성능을 보여주기도 한다. 동작은 그때 그때마다 달라진다.

Using History: LRU


위 예는 0, 1, 2, 0, 1, 3, 0, 3, 1, 2, 1 → Hit ratio: 54.5%

스케줄링 정책에서와 같이 미래에 대한 예측 위해서 과거 사용 이력을 활용해보자. 예를 들어 어떤 프로그램이 가까운 과거에 한 페이지를 접근했다면 가까운 미래에 그 페이지를 접근하게 될 확률이 높다고 생각하는 것이다. → frequency

페이지 교체 정책이 활용할 수 있는 과거 정보 중 하나는 frequency이다. 만약 한 페이지가 여러 차례 접근되었다면, 분명히 어떤 가치가 있기 때문에 교체되면 안 될 것이다.
좀 더 자주 사용되는 페이지의 특징은 접근의 recency(최근성)이다. 더 최근에 접근된 페이지일 수록 가까운 미래에 접근될 확률이 높을 것이다.

이러한 류의 정책은 principle of loacality이라고 부르는 특성에 기반을 둔다.
→ 이 원칙은 프로그램의 행동 양식을 관찰하여 얻은 결과이다. 이 원칙이 말하는 것은 단순하다.
프로그램들은 특정 코드들과 자료 구조를 상당히 빈번하게 접근하는 경향이 있다는 것이다. → 코드의 예로는 반복문 코드를 들 수 있으며, 자료 구조로는 그 반복문에 의해 접근되는 배열을 예로 들 수 있다.
과거의 현상을 보고 어떤 페이지들이 중요한지 판단하고, 내보낼 페이지를 선택할 때 중요한 페이지들은 메모리에 보존하는 것이다. 그리하여 과거 이력에 기반한 교체 알고리즘 부류가 탄생하게 되었다.
Least-Frequently-Used(LFU) 정책은 가장 적은 빈도로 사용된 페이지를 교체한다.
Least-Recently-Used(LRU) 정책은 가장 오래 전에 사용하였던 페이지를 교체한다.
이러한 알고리즘들은 기억하기 쉽다. 이름을 알면 정확하게 그 알고리즘이 어떻게 동작하는지 알 수 있다. 이름이 가지는 탁월한 성질이 아닐 수 없다.

Locality


Temporal locality

이것은 최근에 사용된 데이터나 명령이 다시 사용될 확률이 높다는 뜻입니다. 왜냐하면 프로그램엔 loop문(반복문)이 있기 때문에, 그 안의 코드는 여러 번 사용될 확률이 높은 것이지요,
예: 순환, sub program, stack 등

Spatical locality

특정 클레스터의 기억 장소들에대해 참조가 집중적으로 이루어지는 경향.
프로그램 실행시 접근하는 메모리의 영역은 이미 접근이 이루어진 영역의 근처일 확률이 높다는 프로그램 성격 표현.

Sequential processing, Array 등등.

 

반응형

+ Recent posts