NoSQL 데이터베이스에는 많은 사용 사례가 있는데, 그 중 자주 접하는 사용 사례는 사용자 프로필 저장소 및 세션을 만드는 것입니다. 이 사용 사례는 NoSQL 데이터베이스. 프로필은 종종 유연해야 하고 데이터 변경을 수용해야 합니다. RDBMS에서는 가능하지만, 성능 저하와 함께 데이터를 유지 관리하려면 더 많은 작업이 필요합니다.
커크 커크코넬은 Couchbase를 사용하여 사용자 프로필 저장소와 세션을 만드는 하이레벨 예제를 작성했습니다: 사용자 프로필 저장소: 고급 데이터 모델링. 이러한 개념을 확장하여 Node.js 데이터베이스를 사용하여 사용자 프로필 저장소를 구현하고 카우치베이스 서버.
이 튜토리얼은 2021년 1월 4일 기준으로 다음과 같이 업데이트되었습니다. 에릭 비샤드 Couchbase와 함께 작업하려면 NodeJS SDK 3!
코드를 작성하기 전에 우리가 달성하고자 하는 것이 무엇인지 파악해 보겠습니다.
사용자 데이터를 관리하려면 사용자 프로필 저장소와 세션을 만들고 다른 문서와 연결할 수 있는 방법이 필요합니다. 이 사용자 프로필 저장소 개념에 대한 몇 가지 규칙을 정의해 보겠습니다:
- 사용자 아이디와 비밀번호 같은 계정 데이터를 프로필 문서에 저장합니다.
- 각 사용자 작업 요청과 함께 민감한 사용자 데이터를 전달하세요.
- 설정된 시간 후에 만료되는 세션을 사용합니다.
- 만료 제한이 있는 저장된 세션 문서.
다음 API 엔드포인트를 통해 이 모든 것을 관리할 수 있습니다:
- POST /계정 - 계정 정보로 새 사용자 프로필을 만듭니다.
- POST /로그인 - 계정 정보 확인
- GET /계정 - 계정 정보 가져오기
- POST /blog - 사용자와 연결된 새 블로그 항목 만들기
- GET /blogs - 특정 사용자에 대한 모든 블로그 항목 가져오기
이러한 엔드포인트는 Couchbase Server Node.js SDK를 활용하는 API 백엔드의 일부가 됩니다(이 문서는 버전 3.1.x를 사용하도록 업데이트되었습니다).
Node 및 Express로 API 만들기
Node.js 앱의 프로젝트 디렉토리를 만들고 종속 요소를 설치해 보겠습니다.
|
1 2 |
mkdir 블로그-api && cd 블로그-api && npm init -y npm 설치 카우치베이스 express body-파서 uuid bcryptjs cors --저장 |
이렇게 하면 프로젝트의 작업 디렉터리가 생성되고 새 노드 프로젝트가 초기화됩니다. 종속성에는 카우치베이스용 Node.js SDK 및 Express 프레임워크와 같은 기타 유틸리티 라이브러리 본문 파서 를 사용하여 POST 요청을 통해 JSON 데이터를 수락합니다, uuid 를 사용하여 고유 키를 생성하고 bcryptjs 를 사용하여 악의적인 사용자를 막기 위해 비밀번호를 해시합니다.
애플리케이션을 부트스트랩해 보겠습니다. server.js file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
const 카우치베이스 = require('couchbase') const express = require('express') const uuid = require('uuid') const bodyParser = require('body-parser') const bcrypt = require('bcryptjs') const cors = require('cors') const 앱 = express() 앱.사용(cors()) 앱.사용(bodyParser.json()) 앱.사용(bodyParser.urlencoded({ 확장: true })) const 클러스터 = new 카우치베이스.클러스터('couchbase://localhost', { 사용자 이름: '관리자', 비밀번호: 'password' }) const 버킷 = 클러스터.버킷('blog') const 컬렉션 = 버킷.기본 컬렉션() const 서버 = 앱.듣기(3000, () => 콘솔.정보(`실행 중 on 포트 ${서버.주소().포트}...`)) |
위의 코드는 종속성이 필요하며, 포트 3000에서 실행되는 Express 앱을 다음과 같은 버킷을 사용하여 카우치베이스 서버에 대해 초기화합니다. 블로그.
또한 엔드포인트 중 하나에 N1QL 쿼리 언어를 사용할 것이므로 Couchbase Server에서 인덱스를 만들어야 합니다. 로컬에서 실행 중인 Couchbase Server 웹 콘솔에 액세스하는 경우 localhost:8091을 클릭하고 쿼리 탭을 클릭하고 쿼리 편집기에서 이 문을 실행합니다:
|
1 |
만들기 INDEX `블로그 사용자` 켜기 `기본값`(유형, pid); |
특정 프로필 아이디에 대한 모든 블로그 글을 가져오기 때문에 일반적인 기본 인덱스보다는 이 특정 인덱스를 사용하면 더 나은 성능을 얻을 수 있습니다. 프로덕션 레벨 코드에는 기본 인덱스를 사용하지 않는 것이 좋습니다.
프로필 스토어에 새 사용자 저장하기
사용자 프로필에는 사용자를 설명하는 모든 정보가 포함될 수 있다는 것을 알고 있습니다. 주소, 전화번호, 소셜 미디어 정보 등의 정보. 계정 자격 증명을 기본 프로필 정보와 같은 문서에 저장하는 것은 결코 좋은 생각이 아닙니다. 모든 사용자에 대해 최소 두 개의 문서가 필요하므로 이러한 문서를 어떻게 구성할지 살펴봅시다.
프로필 문서에는 관련 문서에서 참조할 수 있는 키가 있습니다. 이 키는 자동 생성된 UUID입니다: b181551f-071a-4539-96a5-8a3fe8717faf.
프로필 문서에는 두 가지 속성이 포함된 JSON 값이 있습니다: 이메일 및 유형 속성입니다. The 유형 속성은 관계형 데이터베이스에서 테이블이 레코드를 구성하는 방식과 유사하게 문서를 설명하는 중요한 지표입니다. 이는 문서 데이터베이스의 표준 규칙입니다.
|
1 2 3 4 |
{ "type": "프로필", "이메일": "user1234@gmail.com" } |
프로필과 연결된 계정 문서에는 사용자의 이메일과 동일한 키가 있습니다:
user1234@gmail.com 이 문서에는 유형뿐만 아니라 pid 프로필 문서의 키와 함께 다음을 참조하세요. 이메일 및 해시 비밀번호.
|
1 2 3 4 5 6 |
{ "type": "계정", "pid": "b181551f-071a-4539-96a5-8a3fe8717faf", "이메일": "user1234@gmail.com", "비밀번호": "$2a$10$tZ23pbQ1sCX4BknkDIN6NekNo1p/Xo.Vfsttm.USwWYbLAAspeWsC" } |
데이터베이스의 제약 없이 각 문서에 대한 모델과 해당 문서를 연관시키는 전략을 수립했습니다.
계정 생성을 위한 엔드포인트
다음 코드를 server.js file:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
앱.post("/계정", 비동기 (요청, 응답) => { 만약 (!요청.body.이메일) { 반환 응답.상태(401).보내기({ "메시지": "`이메일`이 필요합니다." }) } else 만약 (!요청.body.비밀번호) { 반환 응답.상태(401).보내기({ "메시지": "`비밀번호`가 필요합니다." }) } const id = uuid.v4() const 계정 = { "type": "계정", "pid": id, "이메일": 요청.body.이메일, "비밀번호": bcrypt.해시싱크(요청.body.비밀번호, 10) } const 프로필 = { "type": "프로필", "이메일": 요청.body.이메일 } 기다림 컬렉션.삽입(id, 프로필) .다음(비동기 () => { 기다림 컬렉션.삽입(요청.body.이메일, 계정) .다음((결과) => { 결과.pid = id 반환 응답.보내기(결과) }) .catch(비동기 (e) => { 기다림 컬렉션.제거(id) .다음(() => { 콘솔.오류(`계정 생성 실패, 제거됨: ${id}`) 반환 응답.상태(500).보내기(e) }) .catch(e => 응답.상태(500).보내기(e)) }) }) .catch(e => 응답.상태(500).보내기(e)) }) |
이 코드를 분석해 보겠습니다.
먼저 이메일 그리고 비밀번호 요청에 존재합니다.
다음으로 계정 객체와 프로필 객체를 기반으로 요청에 전송된 데이터를 기반으로 합니다. 그리고 pid 에 저장하고 있습니다. 계정 객체는 고유 키입니다. 이 키는 문서 키로 설정됩니다. 프로필 객체입니다.
그리고 계정 문서는 이메일을 키로 사용합니다. 앞으로 다른 계정 정보(예: 대체 이메일, 소셜 로그인 등)가 필요한 경우 다른 문서를 프로필에 연결할 수 있습니다.
비밀번호를 저장하는 대신 계정 객체를 일반 텍스트로 해시하고, 이를 다시 Bcrypt. 비밀번호는 프로필 객체를 사용하여 보안을 강화하세요. 비밀번호 해싱에 대한 자세한 내용은 다음을 참조하세요. 이 튜토리얼.
데이터가 준비되면 Couchbase에 삽입할 수 있습니다. 이 저장의 목표는 전부 아니면 전무입니다. 우리는 계정 그리고 프로필 문서가 성공적으로 생성되면, 그렇지 않으면 모두 롤백합니다. 성공 여부에 따라 고객에게 일부 정보를 반환합니다.
데이터를 삽입하는 데 N1QL 쿼리를 사용할 수도 있었지만, 성능 저하 없이 CRUD 작업을 사용하는 것이 더 쉽습니다.
민감한 데이터에 세션 토큰 사용
사용자 프로필과 계정이 생성되면 사용자가 로그인하여 데이터를 저장하고 연결할 수 있는 활동을 시작할 수 있습니다.
로그인하여 사용자 프로필을 참조하여 데이터베이스에 저장될 세션을 설정하려고 합니다. 이 문서는 결국 만료되어 데이터베이스에서 제거됩니다.
세션 모델은 다음과 같습니다:
|
1 2 3 4 5 |
{ "type": "세션", "id": "ce0875cb-bd27-48eb-b561-beee33c9f405", "pid": "b181551f-071a-4539-96a5-8a3fe8717faf" } |
이 문서에는 다른 문서와 마찬가지로 다른 유형. 와 마찬가지로 계정 문서에는 pid 사용자 프로필을 참조하는 속성입니다.
이를 가능하게 하는 코드는 로그인 엔드포인트:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
앱.post("/로그인", 비동기 (요청, 응답) => { 만약 (!요청.body.이메일) { 반환 응답.상태(401).보내기({ "메시지": "`이메일`이 필요합니다." }) } else 만약 (!요청.body.비밀번호) { 반환 응답.상태(401).보내기({ "메시지": "`비밀번호`가 필요합니다." }) } 기다림 컬렉션.get(요청.body.이메일) .다음(비동기 (결과) => { 만약 (!bcrypt.비교 동기화(요청.body.비밀번호, 결과.값.비밀번호)) { 반환 응답.상태(500).보내기({ "메시지": "비밀번호가 유효하지 않습니다" }) } var 세션 = { "type": "세션", "id": uuid.v4(), "pid": 결과.값.pid } 기다림 컬렉션.삽입(세션.id, 세션, { "만료": 3600 }) .다음(() => 응답.보내기({ "sid": 세션.id })) .catch(e => 응답.상태(500).보내기(e)) }) .catch(e => 응답.상태(500).보내기(e)) }) |
수신 데이터의 유효성을 검사한 후 이메일 주소로 계정 조회를 수행합니다. 이메일에 대한 데이터가 돌아오면 수신된 비밀번호와 계정 조회에서 반환된 해시된 비밀번호를 비교할 수 있습니다. 이 작업이 성공하면 사용자에 대한 새 세션을 만들 수 있습니다.
이전 삽입 작업과 달리 문서 만료 시간을 1시간(3600초)으로 설정했습니다. 만료가 새로 고쳐지지 않으면 문서가 사라집니다. 이는 사용자가 다시 로그인하여 새 세션을 얻도록 강제하기 때문에 좋습니다. 이 세션 토큰은 향후 모든 요청 시 비밀번호 대신 전달됩니다.
토큰으로 사용자 세션 관리하기
사용자 프로필에 대한 정보를 얻고 프로필에 새로운 항목을 연결하고자 합니다. 이를 위해 세션을 통해 권한을 확인합니다.
미들웨어를 사용하여 세션이 유효한지 확인할 수 있습니다. 미들웨어 함수는 모든 Express 엔드포인트에 추가할 수 있습니다. 이 유효성 검사는 엔드포인트의 HTTP 요청에 액세스할 수 있는 간단한 함수입니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
const 유효성 검사 = 비동기(요청, 응답, 다음) => { const authHeader = 요청.헤더["권한 부여"] 만약 (authHeader) { 베어러토큰 = authHeader.분할(" ") 만약 (베어러토큰.길이 == 2) { 기다림 컬렉션.get(베어러토큰[1]) .다음(비동기(결과) => { 요청.pid = 결과.값.pid 기다림 컬렉션.터치(베어러토큰[1], 3600) .다음(() => 다음()) .catch((e) => 콘솔.오류(e.메시지)) }) .catch((e) => 응답.상태(401).보내기({ "메시지": "잘못된 세션 토큰" })) } } else { 응답.상태(401).보내기({ "메시지": "인증 헤더가 필요합니다." }) } } |
여기서는 인증 헤더 요청을 확인하고 있습니다. 세션 ID(sid)가 있는 유효한 베어러 토큰이 있으면 조회를 수행할 수 있습니다. 세션 문서에 프로필 ID가 있습니다. 세션 조회에 성공하면 요청에 프로필 ID(pid)를 저장합니다.
다음으로 세션 만료를 새로 고치고 미들웨어를 거쳐 다시 엔드포인트로 이동합니다. 세션이 존재하지 않으면 프로필 ID가 전달되지 않고 요청이 실패합니다.
이제 미들웨어를 사용하여 계정 엔드포인트에서 프로필에 대한 정보를 얻을 수 있습니다:
|
1 2 3 4 5 6 7 8 9 |
앱.get("/계정", 유효성 검사, 비동기 (요청, 응답) => { 시도 { 기다림 컬렉션.get(요청.pid) .다음((결과) => 응답.보내기(결과.값)) .catch((e) => 응답.상태(500).보내기(e)) } catch (e) { 콘솔.오류(e.메시지) } }) |
주목하세요. 유효성 검사 가 먼저 발생한 후 나머지 요청이 처리됩니다. 요청의 요청.pid 가 미들웨어에 의해 설정되면 해당 아이디에 대한 특정 프로필 문서를 가져옵니다.
다음으로 엔드포인트를 만들어 사용자를 위한 블로그 글을 추가합니다:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
앱.post("/blog", 유효성 검사, 비동기(요청, 응답) => { 만약(!요청.body.title) { 반환 응답.상태(401).보내기({ "메시지": "`제목`이 필요합니다." }) } else 만약(!요청.body.콘텐츠) { 반환 응답.상태(401).보내기({ "메시지": "`콘텐츠`가 필요합니다." }) } var 블로그 = { "type": "blog", "pid": 요청.pid, "title": 요청.body.title, "content": 요청.body.콘텐츠, "timestamp": (new 날짜()).getTime() } const 고유아이디 = uuid.v4() 컬렉션.삽입(고유아이디, 블로그) .다음(() => 응답.보내기(블로그)) .catch((e) => 응답.상태(500).보내기(e)) }) |
미들웨어가 성공했다고 가정하고, 특정 블로그 객체를 생성합니다. 유형 그리고 pid. 그런 다음 데이터베이스에 저장할 수 있습니다.
특정 사용자의 모든 블로그 글을 쿼리하는 것도 크게 다르지 않습니다:
|
1 2 3 4 5 6 7 8 9 10 11 |
앱.get("/blogs", 유효성 검사, 비동기(요청, 응답) => { 시도 { const 쿼리 = `선택 * FROM `블로그` 어디 유형 = 'blog' AND pid = $PID;` const 옵션 = { 매개변수: { PID: 요청.pid } } 기다림 클러스터.쿼리(쿼리, 옵션) .다음((결과) => 응답.보내기(결과.행)) .catch((e) => 응답.상태(500).보내기(e)) } catch (e) { 콘솔.오류(e.메시지) } }) |
문서 키가 아닌 문서 속성으로 쿼리해야 하므로 이전에 생성한 N1QL 쿼리와 인덱스를 사용하겠습니다.
문서 유형 그리고 pid 를 쿼리에 전달하여 해당 특정 프로필에 대한 모든 문서를 반환합니다.
결론
방금 Node.js와 NoSQL을 사용하여 사용자 프로필 스토어와 세션을 만드는 방법을 살펴보았습니다. 이것은 Kirk가 자신의 블로그에 올린 기사.
앞서 언급했듯이 계정 문서는 동일한 프로필 문서를 참조하는 기본 인증(Facebook 인증 등)을 위한 문서를 가질 수 있는 로그인 자격 증명의 한 형태를 나타낼 수 있습니다. 세션에 UUID를 사용하는 대신 JSON 웹 토큰(JWT) 또는 더 안전한 것을 사용할 수도 있습니다.
그리고 이 시리즈의 다음 튜토리얼에서는 클라이언트 프런트엔드를 만드는 데 도움이 되는 를 입력하세요.
완성된 코드, Postman 컬렉션 및 환경 변수를 사용할 수 있습니다. 카우치베이스 랩스 / 카우치베이스-nodejs-blog-api 리포지토리에 저장합니다.
Node.js와 함께 Couchbase를 사용하는 방법에 대한 자세한 내용은 카우치베이스 개발자 포털.
[...] 링크: https://www.couchbase.com/creating-user-profile-store-with-node-js-nosql-database/ […]