상권's

TIL 44 (OAuth)(2021.11.24) 본문

~2022 작성 글/TIL

TIL 44 (OAuth)(2021.11.24)

라마치 2021. 11. 24. 22:51
정수를 요소로 갖는 문자열을 입력받아 다음의 조건을 만족하는 LIS*의 길이를 리턴해야 합니다.
LIS: 배열의 연속되지 않는 부분 배열 중 모든 요소가 엄격하게 오름차순으로 정렬된 가장 긴 부분 배열(Longest Increasing Subsequence)
배열 [1, 2, 3]의 subseqeunce는 [1], [2], [3], [1, 2], [1, 3], [2, 3], [1, 2, 3] 입니다.
엄격한 오름차순: 배열이 동일한 값을 가진 요소없이 오름차순으로 정렬되어 있는 경우를 말합니다.
Advanced
LIS를 계산하는 효율적인 알고리즘(O(N^2))이 존재합니다.
쉽지 않기 때문에 바로 레퍼런스 코드를 보고 이해하는 데 집중하시기 바랍니다.
subsequence는 문자열 또는 배열같이 순서가 있는 데이터에서 순서의 대소 관계가 유지되는 모든 부분 문자열 또는 부분 배열을 의미합니다.
sequence가 순서 또는 서열을 의미하기 때문에 subsequence는 부분 서열이라고 부르기도 합니다.
반면 substring 또는 subarray는 연속된 형태의 부분 문자열 또는 부분 배열을 의미합니다.
문자열 'abcd'의 subsequence와 substring은 각각 아래와 같습니다.
substring: 'a', 'b', 'c', 'd', 'ab', 'bc', 'cd', 'abc', 'bcd', 'abcd'
subsequence: 'a', 'b', 'c', 'd', 'ab', 'ac', 'ad', 'bc', 'bd', 'cd', 'abc', 'abd', 'acd', 'bcd', 'abcd'

이 문제도 어려워서 레프런스를 보면서 이해하는 방법으로 학습했습니다.

const LIS = function (arr) {
  const N = arr.length;
  // lis[i]는 i에서 끝나는 LIS의 길이를 저장
  // 최소한 각 요소 하나로 LIS를 만들 수 있으므로 1로 초기화한다.

  // arr = [3, 10, 2, 1, 20]
  const lis = Array(N).fill(1);
  // lis = [1, 1, 1, 1, 1]

  for (let i = 1; i < N; i++) {
    // i에서 끝나는 LIS의 길이
    for (let j = 0; j < i; j++) {
      // i 이전의 인덱스만 검사하면 된다.
      // i는 1부터 시작하므로, 짧은 길이부터 검사한다. (bottom-up 방식)
      if (arr[i] > arr[j] && lis[i] < lis[j] + 1) {
        lis[i] += 1;
      }
    }
  }
  return Math.max(...lis);
};
// lis는 해당 위치까지의 배열로 잘랐을 때 만들어지는 lis를 나타낸다.
// 그래서 그 자리까지의 lis에다가 다음 요소가 왔을 때, lis를 충족할 수 있는 지 여부를 확인하고(바로 앞의 요소보다 더 큰지)
// 거기에 1을 더해서 lis의 길이가 만들어지게 된다.

What is difference between subsequence and substring? 출처

코드스테이츠 측에서 설명하는 subsequence와 substring에서는 연속되지 않는다, 연속된다 라는 말로 구분을 하고 있는데, 이 부분에 대해서 와닿지가 않았습니다.

 

substring이란,

문자열 내에 존재하는 연속적인(연속적인) 문자 시퀀스입니다. 문자열 안에 존재하는 문자열입니다.

예를 들어 "tree" 문자열의 하위 문자열은 다음과 같습니다.

 

't', 'r', 'e', ​​'tr', 'tre', 'tree', 're', 'ree', 'ee' 및. ''

 

subarray이란,

배열 내의 연속적인 요소 시퀀스입니다. 빈 배열은 배열의 하위 배열이기도 합니다. 

 

subsequence이란,

나머지 요소의 순서를 변경하지 않고 요소의 다른 시퀀스에서 파생될 수 있는 시퀀스입니다.

즉, 연속성의 규칙이 적용되지 않는 일반화된 부분배열/부분문자열입니다. 따라서 하위 시퀀스는 중간에 있는 요소 중 일부를 삭제하거나 전혀 삭제하지 않고 원래 시퀀스에서 요소의 상대적 순서를 항상 유지하여 다른 시퀀스에서 파생될 수 있습니다.

 

예: {A, B, D}는 {C} 및 {E}를 제거한 후 얻은 시퀀스 {A, B, C, D, E}의 부분 시퀀스 중 하나입니다.


sequelize 추가 학습 출처

앞 서 sequelize에 대해서 학습할 때에는 project bootstrapping을 사용했었는데, 오늘은 DB에 연결하는 다른 방법에 대해서 학습했습니다.

 

Connecting to a database

const { Sequelize } = require('sequelize');
const sequelize = new Sequelize({
  dialect: 'sqlite',
  storage: 'path/to/database.sqlite'
});
const Sequelize = require('sequelize'); // 모듈 갖고 오기
const sequelize = new Sequelize({
    dialect: 'sqlite',
    storage: './db.sqlite'
}) // db랑 연동 시키기 위해서 sequelize 객체를 하나 만들어준다.
// user 테이블을 정희
const User = sequelize.define('User', {
    name:  Sequelize.STRING
})

module.exports = {User, Sequelize, sequelize}

 

public define(modelName(string), attributes(object), options(object)): Model

데이터베이스에 테이블을 나타내는 새 모델을 정의합니다.

 
테이블 열은 두 번째 인수로 지정된 객체에 의해 정의됩니다. 개체의 각 키는 열을 나타냅니다.

Params:

Name Type Attribute Description
modelName string   The name of the model. The model will be stored in sequelize.
models under this name
attributes object   An object, where each attribute is a column of the table. See Model.init
options object optional These options are merged with the default define options provided
to the Sequelize constructor and passed to Model.init()

Return:

Model Newly defined model
const models = require('../models')

// 데이터 베이스를 연동시켜주는 모듈
module.exports = () => {
    return models.sequelize.sync({force: true}) // 프로미스를 리턴
}

public static async sync(options(object)): Promise<Model>

이 모델을 DB에 연결해서 해당 테이블을 만듭니다.

Params:

Name Type Attribute Description
options object optional sync options

Return:

Promise<Model>

OAuth 란?

 

OAuth2.0은 인증을 위한 표준 프로토콜의 한 종류 보안 된 리소스에 액세스하기 위해 클라이언트에게 권한을 제공(Authorization)하는 프로세스를 단순화하는 프로토콜 중 한 방법입니다.

 

OAuth에서 꼭 알아야 할 용어

  • Resource Owner :  액세스 중인 리소스의 유저입니다.
  • Client :  Resource owner를 대신하여 보호된 리소스에 액세스하는 응용프로그램입니다. 클라이언트는 서버, 데스크탑, 모바일 또는 기타 장치에서 호스팅 할 수 있습니다.
  • Resource server :  client의 요청을 수락하고 응답할 수 있는 서버입니다.
  • Authorization server :  Resource server가 액세스 토큰을 발급받는 서버입니다. 즉 클라이언트 및 리소스 소유자를 성공적으로 인증한 후 액세스 토큰을 발급하는 서버를 말합니다.
  • Authorization grant :  클라이언트가 액세스 토큰을 얻을 때 사용하는 자격 증명의 유형입니다.
  • Authorization code :  access token을 발급받기 전에 필요한 code입니다. client ID로 이 code를 받아온 후, client secret과 code를 이용해 Access token 을 받아옵니다.
  • Access token :  보호된 리소스에 액세스하는 데 사용되는 credentials입니다. Authorization code와 client secret을 이용해 받아온 이 Access token으로 이제 resource server에 접근을 할 수 있습니다.
  • Scope :  scope는 토큰의 권한을 정의합니다. 주어진 액세스 토큰을 사용하여 액세스할 수 있는 리소스의 범위입니다.

소셜 로그인 로직 플로우

이번 3일동안 학습했던 내용을 정리한 것입니다.


이번 월, 화, 수동안 쿠키, 세션, 토큰, OAuth에 대해서 학습하면서 axios를 사용했습니다. axios를 사용하면서 어려워했던 작성 방법에 대해서 리뷰해보겠습니다.

 

axios API는 다음과 같은 방법으로 작성할 수 있습니다. 출처

axios(config)

// Send a POST request
axios({
  method: 'post',
  url: '/user/12345',
  data: {
    firstName: 'Fred',
    lastName: 'Flintstone'
  }
});

별칭을 이용한 방법

// Send a GET request (default method)
axios.get('/user/12345');

이러한 방법들로 axios 를 이용할 수 있는데, 이 두가지 방법의 차이를 이해하지 못했었고, 비교적 간단해 보여서 별칭을 계속해서 이용했었습니다.

 

이번에 OAuth 과제를 진행하면서 처음 구현했던 코드입니다.

github에서 authorization code를 받아서 리턴하는 일부입니다.

  axios.post('https://github.com/login/oauth/access_token', 
      { data : {
        client_id : clientID,
        client_secret	: clientSecret,
        code : req.body.authorizationCode
      }},
      { headers : {'Accept': 'application/json' }})
      // github OAuth Apps을 진행할 때에, response body의 타입을 정해줍니다.
  .then((result) => {
    res.status(200).send({accessToken : result.data.access_token})
  })
  .catch((err) => {
    res.status(401).send(err)

이렇게 진행을 했었는데, 원하는 결과값을 받을 수 없었습니다. 반면에 아래와 같이 axios(config)으로 작성하니 정보를 얻을 수 있었습니다.

  axios({
    method : 'POST',
    url : 'https://github.com/login/oauth/access_token',
    headers : {
      'Accept': 'application/json'
    },
    data : {
      client_id : clientID,
      client_secret : clientSecret,
      code : req.body.authorizationCode
    }
  })

이 두 방법의 차이는 다음과 같았습니다.

axios.request(config)

axios.get(url[, config])

axios.delete(url[, config])

axios.head(url[, config])

axios.options(url[, config])

axios.post(url[, data[, config]])

axios.put(url[, data[, config]])

axios.patch(url[, data[, config]])

이처럼 별칭 메서드를 사용할 때는 config에 메서드 및 데이터 속성을 지정할 필요가 없으며, 해당 순서에 맞춰서 코드를 구현해야 합니다.

반면, axios(config)의 경우에는 내부에서 config 들의 순서는 상관없다는 것입니다.

  axios.post('https://github.com/login/oauth/access_token', 
      {
        client_id : clientID,
        client_secret	: clientSecret,
        code : req.body.authorizationCode
      },
      { headers : {'Accept': 'application/json' }})
  .then((result) => {
    res.status(200).send({accessToken : result.data.access_token})
  })
  .catch((err) => {
    res.status(401).send(err)

위의 코드처럼 data 부분을 따로 지정해주지 않고, 바로 데이터를 사용하면 원하는 정보를 얻을 수 있었습니다.

 

더 늦지 않게 코드 작성 순서에 대해서 학습할 수 있어서 다행이지만, 다음부터는 처음부터 꼼꼼하게 학습해서 이런 실수가 생기지 않도록 노력해야겠습니다.

Comments