moment
라이브러리 대신 내장 Date 함수를 사용하면 충분하다.
- JavaScript의 두 날짜를 <,<=,>및>=와 같은 비교 연산자로 직접 비교할 수 있다.
1 | var date1 = new Date('2020-10-23'); |
- 하지만, 등호 연산자를 사용하여 직접 Date 객체를 비교할 수 없다. JavaScript에서 다른 객체는 동일하지 않기 때문이다.
1 | let date1 = new Date(); |
moment
라이브러리 대신 내장 Date 함수를 사용하면 충분하다.
1 | var date1 = new Date('2020-10-23'); |
1 | let date1 = new Date(); |
사실 Gmail API를 사용해서 메일을 보내면 되는 태스크인데, api에 종속되지 않고 메일을 보낼 수 있는 방법이어서 시도해봤다. 서버 없이 프론트에서 form을 이용해 메일을 보낼 수 있는 방법이라 알아두면 유용할 것 같다.
본인 계정의 지메일로 구글 스프레드 시트를 deploy 함. (구글 드라이브에서 확인 가능)
https://docs.google.com/spreadsheets/… → Tools 탭 → Script Editor
* 테스트 후 Manage Deployment
에서 권한 Only Myself 로 설정! *
보내는 사람: Google Apps
를 deploy한 사람
받는 사람: mailData.receiver
(내 form의 name="receiver"
이 받는 사람이기 때문)
it defaults to the email provided by the form’s data attribute
var mailData = e.parameters
=== FormData body
e
: formData
e
is the data received from the POST
e.parameters
=== mailData : formData.data
shorter name for form data
1 | /* |
위는 내 폼 형식에 맞게 중요한 부분만 적어두었는데, 단계별로 진행해보고 싶다면 밑 레퍼런스를 참고하면 된다.
bisect
: 정렬된 배열을 유지하면서 특정 값을 배열에 추가할 때, 몇 번째 인덱스에 넣어야하는지 반환하는 함수
1 | from bisect import bisect_left, bisect_right |
bisect_left(literable, value)
: 왼쪽 인덱스를 구하기
bisect_right(literable, value)
: 오른쪽 인덱스를 구하기
redirect uri
로 리다이렉트 시키면서 access token
을 애플리케이션에 보낸다.access token
을 이용해서 구글 API를 호출하여 원하는 정보를 얻는다.access token
을 요청하고,token
을 추출한다.token
을 보낸다.구글 API를 호출하기 위해서는 가장 먼저, 구글에 API를 사용하고자 하는 어플리케이션을 클라이언트로 등록하고 client_id
를 발급받아야 한다.
[http://localhost:3000/](http://localhost:3000/)
) → 클라이언트 ID 생성Google Authorization Server에 접속하여 사용자 token
을 받아야 한다.
CLIENT_ID
: 위에서 발급받은 클라이언트 IDAUTHORIZE_URI
: 구글에서 OAuth를 제공하는 기본 URIqueryStr
: 위 AUTHORIZE_URI
에 붙일 사용자 개인 정보 (쿼리), URL 생성용client_id
redirect_uri
: 실습 애플리케이션은 페이지가 하나 밖에 없는 SPA이기 때문에, 현재 URL을 redirect_uri
로 설정response_type
: 어떤 OAuth 방식을 사용하는지를 결정하는 속성으로, token
으로 설정하면 implicit grant 방식이 적용scope
: 사용자의 어떤 데이터와 어떤 작업에 대한 권한을 요청하는지, 즉 어떤 구글 API를 요청하는지loginUrl
: AUTHORIZE_URI
에 queryStr
을 붙여 만든 구글 로그인 authorization URL1 | const CLIENT_ID = "YOUR CLIENT ID"; |
Google Authorization Server가 redirect URI
로 access token
을 보내주면, 이를 이용해 token validation을 진행한다.
window.location.hash
를 이용해 가져온다.1 | const { access_token } = qs.parse(window.location.hash.substring(1)); |
access token을 이용해 구글 API를 호출한다.
Bearer
문자열을 access token
앞에 붙여 Authorization header
로 넘긴다.1 | const PEOPLE_URI = "https://people.googleapis.com/v1/contactGroups"; |
qs
npm 패키지: A querystring parsing and stringifying library with some added security.
1 | import React, { useState, useEffect } from "react"; |
OAuth 2.0 Flow: Client-side web apps | YouTube Data API
Using OAuth 2.0 to Access Google APIs | Google Identity
OAuth 2.0 for Client-side Web Applications | Google Identity
보통, cra typescript를 위해서
1 | npx create-react-app [project-name] --template typescript |
를 쓰지만,
이번에는 직접 만들어보면서 react library의 작동 원리를 살펴보려고 한다.
npm을 세팅한다.
1 | npm init -y // -y로 default 설정 적용 |
npm pkg를 설치한다.
typescript
1 | npm i --s typescript @types/react @types/react-dom |
react
1 | npm i --s react react-dom |
babel
1 | npm i --s-d babel-loader @babel/core @babel/preset-env @babel/preset-react @babel/preset-typescript |
webpack
1 | npm i --s-d webpack webpack-dev-server webpack-cli |
타입스크립트 설정하기
타입스크립트 설정 파일을 만들기 위해,
tsc --init
node_modules/.bin/tsc --init
을 수행한다.
그럼 tsconfig.json
이 생성되는데, 다음과 같이 주석을 해지해주면 된다. (꼭 밑과 같지 않아도, 경우에 맞게/ 입맛에 맞게 설정해두면 두고두고 용이함!)
1 | { |
리액트 Entry File 설정
보통 cra 시 다음과 같은 디렉토리를 갖게 된다. (직접 만들어야 하는 파일만 명시해둠)
1 | node_modules |
따라서 먼저 Component를 만들어보자.
1 | import * as React from "react";// 함수형 컴포넌트const App: React.FunctionComponent<{ name: string }> = ({ children, name }) => ( <div title={name}>{children}</div>);export default App; |
그리고 html에서 script로 읽을 index.js
를 생성하자.
1 | import * as React from "react";import * as ReactDOM from "react-dom";import App from "./App";// 클래스형 컴포넌트class Root extends React.Component { render() { return ( <div> <App name="test"> test </App> </div> ); }}ReactDOM.render(<Root />, document.getElementById("root")); |
원래는 따로 클래스 정의 없이 ReactDOM.render()
에 클래스 컴포넌트 렌더 안에 들어있는 코드를 적어 바로 컴포넌트를 전달한다. 축약하면 다음과 같다.
1 | ReactDOM.render( <App />, document.getElementById('root')); |
이제 최종적으로 js 파일을 전달할 public/index.html
을 정의하자!
1 | <!DOCTYPE html><html lang="en"> <head> <meta charset="UTF-8" /> <title>채용 프로세스 이메일 템플릿</title> </head> <body> <div id="root"></div> <!-- 번들 파일 추가 --> <script src="./bundle.js"></script> </body></html> |
html은 id="root"
를 통해 index 파일의 document.getElementById("root")
를 읽어나가기 때문에, 따로 script를 적용하지 않는다. 하지만, 이 src/index.js
를 읽기 위해 webpack 번들러를 설정하였으므로, 해당 bundle.js
파일을 script로 적용해야 한다!
꼭
<div id="root"></div>
밑에 전달해야 한단다.
내 생각에는 html을 읽는 순서 때문에 그런듯.
Babel 설정하기
trans-compile을 위해 .babelrc
나 babel.config.js
로 설정한다.
1 | { "presets": [ "@babel/preset-env", "@babel/preset-react", "@babel/preset-typescript" ]} |
webpack 설정하기
이 모든 과정을 연결하기 위해 번들러 webpack을 설정한다. 바벨과 같이 webpack.config.js
를 루트 폴더에 만들면 됨!
1 | module.exports = { entry: ["./src/index.tsx"], output: { filename: "bundle.js", publicPath: "/", path: __dirname + "dist" }, module: { rules: [ { test: /\.(js|jsx|ts|tsx)$/, exclude: /node_modules/, use: ["babel-loader"] } ] }, resolve: { extensions: ["*", ".js", ".jsx", ".ts", ".tsx"] }}; |
resolve
: 확장자나 경로를 알아서 처리할 수 있도록 설정하는 옵션이다.module
: 이 옵션에 설치한 ts-loader와 babel-loader를 설정하면 된다. loader들은 오른쪽에서 왼쪽 방향으로 적용되기 때문에 ts-loader를 babel-loader보다 오른쪽에 위치시켜야 한다.output
: 번들화 된 파일을 export할 경로와 파일명을 설정한다.
마지막으로, package.json
에서 script
를 설정해주면 된다.
npm start
또는 npm run start
로 script를 실행할 수 있도록 다음과 같이 설정하면 됨!
npm run 시 script 내부의 명령어을 적용할 수 있다!
1 | "scripts": { "start": "webpack-dev-server --config ./webpack.config.js --mode development", "test": "echo \"Error: no test specified\" && exit 1" }, |
웹팩 설정에서 사용된 inline
모드는 무엇일까?
네트워크 방화벽 구성과 동일하게 모든 트래픽이 해당 보안장비를 거쳐야만 목적지로 전송될 수 있도록 네트워크를 구성하는 방식
hot
모드는?
Hot Module Replacement(HMR)는 응용프로그램 실행 중에 추가 또는 제거된 모듈들을 페이지 리로드 없이 교체하는 기능입니다.
리액트 - 타입스크립트 시작하기 (without CRA)
위 블로그 기반으로,
CRA없이 React + TypeScript 셋팅하기! - chanyeong
위 블로그는 심화형임
정규식 문자열 앞에 r
문자를 삽입하면 이 정규식은 Raw String 규칙에 의하여 백슬래시 2개 대신 1개만 써도 2개를 쓴 것과 동일한 의미를 갖게 된다.
1 | re.sub(r'\') ## '\\' |
※ 만약 백슬래시를 사용하지 않는 정규식이라면 r의 유무에 상관없이 동일한 정규식이 될 것이다.
re.sub(pattern, repl, string, count=0, flag=0)
: pattern
을 repl
로 대체한 string
을 반환한다.
repl
내 \
가 존재할 경우 backslash escape 가 적용된다.알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거
1 | re.sub(r"[^a-z0-9-_.]", "", answer) |
마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환
1 | re.sub('\.\.+', '.', answer) |
마침표(.)가 처음이나 끝에 위치한다면 제거
1 | re.sub("^[.]|[.]$", "", answer) |
첫째 줄에 단어의 개수 N이 주어진다. (1 ≤ N ≤ 20,000)
둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나씩 주어진다. 주어지는 문자열의 길이는 50을 넘지 않는다.
1 | import sys |
set
를 이용하고,sort
를 구현하는데, set
에는 sort
가 없으므로 sorted
를 이용해야 한다.1 | s = sorted(s, key=(lambda x: (len(x), x))) |
조건에 따라 정렬하여 단어들을 출력한다. 단, 같은 단어가 여러 번 입력된 경우에는 한 번씩만 출력한다.
1 | for word in s: |
첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다.
둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.
1 | import sys |
sort
함수에 key
를 이용하면 쉽게 해결되는데, 이를 위해서 sort
함수를 살펴보자.
sort()
Basic Syntax
1 list.sort(key=..., reverse=...)Parameters
key
: sort 수행 시 비교 를 위해 적용되는 함수
e.g. 1D
1 list.sort(key=len)
len
함수에 따라 리스트의 요소의 길이가 정렬 기준이 된다.e.g. 2D
tuple의 경우, 첫번째 파라미터가 디폴트로 정렬 기준이 된다.
1
2
3
4
5
6
7
8
9
10 # random list
random = [(2, 2), (3, 4), (4, 1), (1, 3)]
# sort list with key
random.sort(key=(lambda x: x[1]))
### lambda to function declaration
# key=takeSecond
# def takeSecond(elem):
# return elem[1]위의 예시에서는, 튜플
x
의 2번째 파라미터를 기준으로 정렬을 수행한다.
reverse
: 기본 오름차순,True
시 내림차순Return Value
None
👉 원본을 수정한다.sorted()
sorted list
👉 원본을 수정하지 않고, 새로운 리스트를 반환한다.
위에 따라 key
를 지정해서 sort
함수를 커스터마이징하면,
1 | s.sort(key=(lambda x: (x[0], x[1]))) |
이는 즉, 튜플 x
의 1번째 인자를 첫 기준으로 두고, 2번째 인자를 두번째 기준으로 둔다 는 의미이다.
첫째 줄부터 N개의 줄에 점을 정렬한 결과를 출력한다.
1 | for i in s: |