Node.js에서 보안 REST API 만들기

게시 됨: 2022-03-11

API(응용 프로그래밍 인터페이스)는 어디에나 있습니다. 이를 통해 소프트웨어는 재사용성은 말할 것도 없고 확장성의 핵심 요소인 내부 또는 외부의 다른 소프트웨어와 일관되게 통신할 수 있습니다.

오늘날 온라인 서비스에 공개 API가 있는 것은 매우 일반적입니다. 이를 통해 다른 개발자는 소셜 미디어 로그인, 신용 카드 결제 및 행동 추적과 같은 기능을 쉽게 통합할 수 있습니다. 이를 위해 사용하는 사실상 의 표준을 REST(REpresentational State Transfer)라고 합니다.

여러 플랫폼과 프로그래밍 언어(예: ASP.NET Core, Laravel(PHP) 또는 Bottle(Python))를 작업에 사용할 수 있지만 이 자습서에서는 다음을 사용하여 기본적이지만 안전한 REST API 백엔드를 빌드합니다. 다음 스택:

  • 독자가 이미 어느 정도 익숙해야 하는 Node.js
  • Node.js에서 일반적인 웹 서버 작업 구축을 크게 단순화하고 REST API 백엔드 구축의 표준 요금인 Express
  • 백엔드를 MongoDB 데이터베이스에 연결하는 Mongoose

이 자습서를 따르는 개발자는 터미널(또는 명령 프롬프트)에도 익숙해야 합니다.

참고: 여기서는 프론트엔드 코드베이스를 다루지 않겠지만 백엔드가 JavaScript로 작성되었다는 사실 덕분에 전체 스택에서 코드(예: 개체 모델)를 공유하는 것이 편리합니다.

REST API 분석

REST API는 상태 비저장 작업의 공통 세트를 사용하여 데이터에 액세스하고 조작하는 데 사용됩니다. 이러한 작업은 HTTP 프로토콜에 필수적이며 완전한 일대일 방식은 아니지만 필수적인 CRUD(생성, 읽기, 업데이트 및 삭제) 기능을 나타냅니다.

  • POST (리소스 생성 또는 일반적으로 데이터 제공)
  • GET (리소스 또는 개별 리소스의 인덱스 검색)
  • PUT (리소스 생성 또는 교체)
  • PATCH (리소스 업데이트/수정)
  • DELETE (리소스 제거)

이러한 HTTP 작업과 리소스 이름을 주소로 사용하여 각 작업에 대한 끝점을 만들어 REST API를 빌드할 수 있습니다. 그리고 패턴을 구현함으로써 우리는 코드를 빠르게 발전시키고 나중에 유지할 수 있는 안정적이고 이해하기 쉬운 기반을 갖게 될 것입니다. 이전에 언급했듯이 동일한 기반이 타사 기능을 통합하는 데 사용되며 대부분이 마찬가지로 REST API를 사용하여 이러한 통합을 더 빠르게 만듭니다.

지금은 Node.js를 사용하여 보안 REST API 생성을 시작하겠습니다!

이 자습서에서는 users 라는 리소스에 대해 매우 일반적이고 매우 실용적인 REST API를 만들 것입니다.

리소스의 기본 구조는 다음과 같습니다.

  • id (자동 생성된 UUID)
  • firstName
  • lastName
  • email
  • password
  • permissionLevel (이 사용자는 무엇을 할 수 있습니까?)

그리고 해당 리소스에 대해 다음 작업을 생성합니다.

  • 엔드포인트 /usersPOST (새 사용자 생성)
  • 엔드포인트에서 GET /users (모든 사용자 나열)
  • /users/:userId 엔드포인트에서 GET (특정 사용자 가져오기)
  • 엔드포인트의 PATCH /users/:userId (특정 사용자에 대한 데이터 업데이트)
  • /users/:userId 끝점에서 DELETE (특정 사용자 제거)

또한 액세스 토큰에 JSON 웹 토큰(JWT)을 사용할 것입니다. 이를 위해 사용자의 이메일과 비밀번호를 예상하고 그 대가로 특정 작업에서 인증에 사용되는 토큰을 생성하는 auth 라는 또 다른 리소스를 생성합니다. (Java의 보안 REST 애플리케이션을 위한 JWT에 대한 Dejan Milosevic의 훌륭한 기사는 이에 대해 더 자세히 설명합니다. 원칙은 동일합니다.)

REST API 튜토리얼 설정

먼저 최신 Node.js 버전이 설치되어 있는지 확인합니다. 이 기사에서는 버전 14.9.0을 사용할 것입니다. 이전 버전에서도 작동할 수 있습니다.

다음으로 MongoDB가 설치되어 있는지 확인합니다. 여기에서 사용되는 Mongoose 및 MongoDB의 세부 사항은 설명하지 않겠지만 기본을 실행하려면 서비스가 아닌 대화식 모드(즉, 명령줄에서 mongo )로 서버를 시작하기만 하면 됩니다. 이 튜토리얼의 어느 시점에서 Node.js 코드를 통하지 않고 MongoDB와 직접 상호 작용해야 하기 때문입니다.

참고: MongoDB를 사용하면 일부 RDBMS 시나리오에서와 같이 특정 데이터베이스를 생성할 필요가 없습니다. Node.js 코드의 첫 번째 삽입 호출은 생성을 자동으로 트리거합니다.

이 자습서에는 작업 프로젝트에 필요한 모든 코드가 포함되어 있지 않습니다. 대신 컴패니언 리포지토리를 복제하고 읽을 때 강조 표시를 따라하기만 하면 되지만 원하는 경우 필요에 따라 리포지토리에서 특정 파일과 스니펫을 복사할 수도 있습니다.

터미널에서 결과로 나오는 rest-api-tutorial/ 폴더로 이동합니다. 프로젝트에 세 개의 모듈 폴더가 포함되어 있음을 알 수 있습니다.

  • common (모든 공유 서비스 및 사용자 모듈 간에 공유되는 정보 처리)
  • users (사용자에 관한 모든 것)
  • auth (JWT 생성 및 로그인 흐름 처리)

이제 npm install (또는 있는 경우 yarn )을 실행합니다.

축하합니다. 이제 간단한 REST API 백엔드를 실행하는 데 필요한 모든 종속성과 설정이 있습니다.

사용자 모듈 만들기

MongoDB용 ODM(객체 데이터 모델링) 라이브러리인 Mongoose를 사용하여 사용자 스키마 내에서 사용자 모델을 생성합니다.

먼저 /users/models/users.model.js 에 Mongoose 스키마를 생성해야 합니다.

 const userSchema = new Schema({ firstName: String, lastName: String, email: String, password: String, permissionLevel: Number });

스키마를 정의하면 스키마를 사용자 모델에 쉽게 연결할 수 있습니다.

 const userModel = mongoose.model('Users', userSchema);

그런 다음 이 모델을 사용하여 Express 엔드포인트 내에서 원하는 모든 CRUD 작업을 구현할 수 있습니다.

users/routes.config.js 에서 경로를 정의하여 "사용자 생성" 작업을 시작하겠습니다.

 app.post('/users', [ UsersController.insert ]);

이것은 기본 index.js 파일의 Express 앱으로 가져옵니다. UsersController 객체는 UsersController 에 정의된 비밀번호를 적절하게 해시하는 컨트롤러에서 가져 /users/controllers/users.controller.js .

 exports.insert = (req, res) => { let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512',salt) .update(req.body.password) .digest("base64"); req.body.password = salt + "$" + hash; req.body.permissionLevel = 1; UserModel.createUser(req.body) .then((result) => { res.status(201).send({id: result._id}); }); };

이 시점에서 서버( npm start )를 실행하고 일부 JSON 데이터와 함께 POST 요청을 /users 에 전송하여 Mongoose 모델을 테스트할 수 있습니다.

 { "firstName" : "Marcos", "lastName" : "Silva", "email" : "[email protected]", "password" : "s3cr3tp4sswo4rd" }

이를 위해 사용할 수 있는 몇 가지 도구가 있습니다. Insomnia(아래에서 설명) 및 Postman은 널리 사용되는 GUI 도구이며 curl 은 일반적인 CLI 선택입니다. 예를 들어 브라우저의 내장 개발 도구 콘솔에서 JavaScript를 사용할 수도 있습니다.

 fetch('http://localhost:3600/users', { method: 'POST', headers: { "Content-type": "application/json" }, body: JSON.stringify({ "firstName": "Marcos", "lastName": "Silva", "email": "[email protected]", "password": "s3cr3tp4sswo4rd" }) }) .then(function(response) { return response.json(); }) .then(function(data) { console.log('Request succeeded with JSON response', data); }) .catch(function(error) { console.log('Request failed', error); });

이 시점에서 유효한 게시물의 결과는 생성된 사용자의 ID인 { "id": "5b02c5c84817bf28049e58a3" } 입니다. 또한 users/models/users.model.js 의 모델에 createUser 메소드를 추가해야 합니다.

 exports.createUser = (userData) => { const user = new User(userData); return user.save(); };

설정이 완료되었습니다. 이제 사용자가 존재하는지 확인해야 합니다. 이를 위해 users/:userId 엔드포인트에 대해 "get user by id" 기능을 구현할 것입니다.

먼저 /users/routes/config.js 에 경로를 생성합니다.

 app.get('/users/:userId', [ UsersController.getById ]);

그런 다음 /users/controllers/users.controller.js 에 컨트롤러를 생성합니다.

 exports.getById = (req, res) => { UserModel.findById(req.params.userId).then((result) => { res.status(200).send(result); }); };

마지막으로 /users/models/users.model.js 의 모델에 findById 메서드를 추가합니다.

 exports.findById = (id) => { return User.findById(id).then((result) => { result = result.toJSON(); delete result._id; delete result.__v; return result; }); };

응답은 다음과 같을 것입니다.

 { "firstName": "Marcos", "lastName": "Silva", "email": "[email protected]", "password": "Y+XZEaR7J8xAQCc37nf1rw==$p8b5ykUx6xpC6k8MryDaRmXDxncLumU9mEVabyLdpotO66Qjh0igVOVerdqAh+CUQ4n/E0z48mp8SDTpX2ivuQ==", "permissionLevel": 1, "id": "5b02c5c84817bf28049e58a3" }

해시된 비밀번호를 볼 수 있습니다. 이 튜토리얼에서는 비밀번호를 보여주지만 가장 확실한 모범 사례는 비밀번호가 해시된 경우에도 절대 비밀번호를 공개하지 않는 것입니다. 우리가 볼 수 있는 또 다른 것은 나중에 사용자 permissionLevel 을 처리하는 데 사용할 permissionLevel 입니다.

위에서 설명한 패턴을 반복하여 이제 사용자를 업데이트하는 기능을 추가할 수 있습니다. PATCH 작업을 사용하면 변경하려는 필드만 보낼 수 있기 때문입니다. 따라서 경로는 /users/:userid 에 대한 PATCH 가 되며 변경하려는 필드를 보낼 것입니다. 또한 변경 사항은 해당 사용자 또는 관리자로 제한되어야 하고 관리자만 permissionLevel 을 변경할 수 있어야 하므로 몇 가지 추가 유효성 검사를 구현해야 합니다. 지금은 건너뛰고 auth 모듈을 구현하면 다시 돌아갑니다. 현재 컨트롤러는 다음과 같습니다.

 exports.patchById = (req, res) => { if (req.body.password){ let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); req.body.password = salt + "$" + hash; } UserModel.patchUser(req.params.userId, req.body).then((result) => { res.status(204).send({}); }); };

기본적으로 요청이 성공했음을 나타내기 위해 응답 본문이 없는 HTTP 코드 204를 보냅니다.

그리고 모델에 patchUser 메서드를 추가해야 합니다.

 exports.patchUser = (id, userData) => { return User.findOneAndUpdate({ _id: id }, userData); };

사용자 목록은 다음 컨트롤러에 의해 /users/ 에서 GET 으로 구현됩니다.

 exports.list = (req, res) => { let limit = req.query.limit && req.query.limit <= 100 ? parseInt(req.query.limit) : 10; let page = 0; if (req.query) { if (req.query.page) { req.query.page = parseInt(req.query.page); page = Number.isInteger(req.query.page) ? req.query.page : 0; } } UserModel.list(limit, page).then((result) => { res.status(200).send(result); }) };

해당 모델 방법은 다음과 같습니다.

 exports.list = (perPage, page) => { return new Promise((resolve, reject) => { User.find() .limit(perPage) .skip(perPage * page) .exec(function (err, users) { if (err) { reject(err); } else { resolve(users); } }) }); };

결과 목록 응답의 구조는 다음과 같습니다.

 [ { "firstName": "Marco", "lastName": "Silva", "email": "[email protected]", "password": "z4tS/DtiH+0Gb4J6QN1K3w==$al6sGxKBKqxRQkDmhnhQpEB6+DQgDRH2qr47BZcqLm4/fphZ7+a9U+HhxsNaSnGB2l05Oem/BLIOkbtOuw1tXA==", "permissionLevel": 1, "id": "5b02c5c84817bf28049e58a3" }, { "firstName": "Paulo", "lastName": "Silva", "email": "[email protected]", "password": "wTsqO1kHuVisfDIcgl5YmQ==$cw7RntNrNBNw3MO2qLbx959xDvvrDu4xjpYfYgYMxRVDcxUUEgulTlNSBJjiDtJ1C85YimkMlYruU59rx2zbCw==", "permissionLevel": 1, "id": "5b02d038b653603d1ca69729" } ]

그리고 구현될 마지막 부분은 /users/:userIdDELETE 입니다.

삭제 컨트롤러는 다음과 같습니다.

 exports.removeById = (req, res) => { UserModel.removeById(req.params.userId) .then((result)=>{ res.status(204).send({}); }); };

이전과 마찬가지로 컨트롤러는 HTTP 코드 204를 반환하고 콘텐츠 본문은 확인하지 않습니다.

해당 모델 메서드는 다음과 같아야 합니다.

 exports.removeById = (userId) => { return new Promise((resolve, reject) => { User.deleteMany({_id: userId}, (err) => { if (err) { reject(err); } else { resolve(err); } }); }); };

이제 사용자 리소스를 조작하는 데 필요한 모든 작업이 완료되었으며 사용자 컨트롤러가 완료되었습니다. 이 코드의 주요 아이디어는 REST 패턴 사용의 핵심 개념을 제공하는 것입니다. 일부 유효성 검사 및 권한을 구현하려면 이 코드로 돌아가야 하지만 먼저 보안 구축을 시작해야 합니다. 인증 모듈을 만들어 봅시다.

인증 모듈 만들기

권한 및 유효성 검증 미들웨어를 구현하여 users 모듈을 보호하기 전에 현재 사용자에 대한 유효한 토큰을 생성할 수 있어야 합니다. 유효한 이메일과 비밀번호를 제공하는 사용자에 대한 응답으로 JWT를 생성합니다. JWT는 사용자가 반복적으로 유효성을 검사하지 않고도 여러 요청을 안전하게 수행하도록 하는 데 사용할 수 있는 놀라운 JSON 웹 토큰입니다. 일반적으로 만료 시간이 있으며 통신 보안을 유지하기 위해 몇 분마다 새 토큰이 다시 생성됩니다. 그러나 이 자습서에서는 토큰 새로 고침을 생략하고 로그인당 단일 토큰으로 간단하게 유지합니다.

먼저 /auth 리소스에 대한 POST 요청에 대한 엔드포인트를 생성합니다. 요청 본문에는 사용자 이메일과 비밀번호가 포함됩니다.

 { "email" : "[email protected]", "password" : "s3cr3tp4sswo4rd2" }

컨트롤러를 사용하기 전에 /authorization/middlewares/verify.user.middleware.js 에서 사용자를 확인해야 합니다.

 exports.isPasswordAndUserMatch = (req, res, next) => { UserModel.findByEmail(req.body.email) .then((user)=>{ if(!user[0]){ res.status(404).send({}); }else{ let passwordFields = user[0].password.split('$'); let salt = passwordFields[0]; let hash = crypto.createHmac('sha512', salt).update(req.body.password).digest("base64"); if (hash === passwordFields[1]) { req.body = { userId: user[0]._id, email: user[0].email, permissionLevel: user[0].permissionLevel, provider: 'email', name: user[0].firstName + ' ' + user[0].lastName, }; return next(); } else { return res.status(400).send({errors: ['Invalid email or password']}); } } }); };

그렇게 하면 컨트롤러로 이동하여 JWT를 생성할 수 있습니다.

 exports.login = (req, res) => { try { let refreshId = req.body.userId + jwtSecret; let salt = crypto.randomBytes(16).toString('base64'); let hash = crypto.createHmac('sha512', salt).update(refreshId).digest("base64"); req.body.refreshKey = salt; let token = jwt.sign(req.body, jwtSecret); let b = Buffer.from(hash); let refresh_token = b.toString('base64'); res.status(201).send({accessToken: token, refreshToken: refresh_token}); } catch (err) { res.status(500).send({errors: err}); } };

이 튜토리얼에서 토큰을 새로 고치지는 않겠지만 컨트롤러는 이러한 생성을 가능하게 하여 후속 개발에서 더 쉽게 구현할 수 있도록 설정되었습니다.

이제 필요한 것은 경로를 만들고 /authorization/routes.config.js 에서 적절한 미들웨어를 호출하는 것입니다.

 app.post('/auth', [ VerifyUserMiddleware.hasAuthValidFields, VerifyUserMiddleware.isPasswordAndUserMatch, AuthorizationController.login ]);

응답에는 accessToken 필드에 생성된 JWT가 포함됩니다.

 { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI1YjAyYzVjODQ4MTdiZjI4MDQ5ZTU4YTMiLCJlbWFpbCI6Im1hcmNvcy5oZW5yaXF1ZUB0b3B0YWwuY29tIiwicGVybWlzc2lvbkxldmVsIjoxLCJwcm92aWRlciI6ImVtYWlsIiwibmFtZSI6Ik1hcmNvIFNpbHZhIiwicmVmcmVzaF9rZXkiOiJiclhZUHFsbUlBcE1PakZIRG1FeENRPT0iLCJpYXQiOjE1MjY5MjMzMDl9.mmNg-i44VQlUEWP3YIAYXVO-74803v1mu-y9QPUQ5VY", "refreshToken": "U3BDQXBWS3kyaHNDaGJNanlJTlFkSXhLMmFHMzA2NzRsUy9Sd2J0YVNDTmUva0pIQ0NwbTJqOU5YZHgxeE12NXVlOUhnMzBWMGNyWmdOTUhSaTdyOGc9PQ==" }

토큰을 생성하면 Bearer ACCESS_TOKEN 형식을 사용하여 Authorization 헤더 내에서 토큰을 사용할 수 있습니다.

권한 및 검증 미들웨어 생성

가장 먼저 정의해야 할 것은 users 리소스를 사용할 수 있는 사람입니다. 처리해야 하는 시나리오는 다음과 같습니다.

  • 사용자 생성을 위한 공개(등록 절차). 이 시나리오에서는 JWT를 사용하지 않습니다.
  • 로그인한 사용자와 관리자가 해당 사용자를 업데이트할 수 있도록 비공개입니다.
  • 사용자 계정을 제거하기 위한 관리자 전용입니다.

이러한 시나리오를 식별했으면 먼저 사용자가 유효한 JWT를 사용하는 경우 항상 사용자의 유효성을 검사하는 미들웨어가 필요합니다. /common/middlewares/auth.validation.middleware.js 의 미들웨어는 다음과 같이 간단할 수 있습니다.

 exports.validJWTNeeded = (req, res, next) => { if (req.headers['authorization']) { try { let authorization = req.headers['authorization'].split(' '); if (authorization[0] !== 'Bearer') { return res.status(401).send(); } else { req.jwt = jwt.verify(authorization[1], secret); return next(); } } catch (err) { return res.status(403).send(); } } else { return res.status(401).send(); } };

요청 오류를 처리하기 위해 HTTP 오류 코드를 사용합니다.

  • 잘못된 요청에 대한 HTTP 401
  • 유효하지 않은 토큰이 있는 유효한 요청에 대한 HTTP 403 또는 유효하지 않은 권한이 있는 유효한 토큰

비트 AND 연산자(비트마스킹)를 사용하여 권한을 제어할 수 있습니다. 필요한 각 권한을 2의 거듭제곱으로 설정하면 32비트 정수의 각 비트를 단일 권한으로 처리할 수 있습니다. 그런 다음 관리자는 권한 값을 2147483647로 설정하여 모든 권한을 가질 수 있습니다. 그러면 해당 사용자는 모든 경로에 액세스할 수 있습니다. 다른 예로, 권한 값이 7로 설정된 사용자는 값 1, 2 및 4(0, 1, 2의 2승)에 대한 비트로 표시된 역할에 대한 권한을 갖습니다.

이를 위한 미들웨어는 다음과 같습니다.

 exports.minimumPermissionLevelRequired = (required_permission_level) => { return (req, res, next) => { let user_permission_level = parseInt(req.jwt.permission_level); let user_id = req.jwt.user_id; if (user_permission_level & required_permission_level) { return next(); } else { return res.status(403).send(); } }; };

미들웨어는 일반적입니다. 사용자 권한 수준과 필요한 권한 수준이 최소 1비트에서 일치하면 결과가 0보다 크며 작업을 계속 진행할 수 있습니다. 그렇지 않으면 HTTP 코드 403이 반환됩니다.

이제 /users/routes.config.js 의 사용자 모듈 경로에 인증 미들웨어를 추가해야 합니다.

 app.post('/users', [ UsersController.insert ]); app.get('/users', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(PAID), UsersController.list ]); app.get('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.getById ]); app.patch('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(FREE), PermissionMiddleware.onlySameUserOrAdminCanDoThisAction, UsersController.patchById ]); app.delete('/users/:userId', [ ValidationMiddleware.validJWTNeeded, PermissionMiddleware.minimumPermissionLevelRequired(ADMIN), UsersController.removeById ]);

이것으로 REST API의 기본 개발을 마칩니다. 이제 모든 것을 테스트하는 일만 남았습니다.

불면증으로 실행 및 테스트

Insomnia는 좋은 무료 버전이 있는 괜찮은 REST 클라이언트입니다. 물론 가장 좋은 방법은 코드 테스트를 포함하고 프로젝트에 적절한 오류 보고를 구현하는 것이지만, 타사 REST 클라이언트는 오류 보고 및 디버깅 서비스를 사용할 수 없을 때 타사 솔루션을 테스트하고 구현하는 데 적합합니다. 여기에서 응용 프로그램의 역할을 수행하고 API에서 진행 중인 작업에 대한 통찰력을 얻기 위해 이를 사용할 것입니다.

사용자를 생성하려면 필수 필드를 적절한 엔드포인트에 POST 하고 생성된 ID를 나중에 사용할 수 있도록 저장하기만 하면 됩니다.

사용자 생성을 위한 적절한 데이터로 요청

API는 사용자 ID로 응답합니다.

userID로 확인 응답

이제 /auth/ 엔드포인트를 사용하여 JWT를 생성할 수 있습니다.

로그인 데이터로 요청

응답으로 토큰을 받아야 합니다.

해당 JSON 웹 토큰이 포함된 확인

accessToken 을 잡고 Bearer 접두사(공백 기억)를 붙이고 Authorization 아래의 요청 헤더에 추가합니다.

인증 JWT가 포함된 전송할 헤더 설정

권한 미들웨어를 구현했으므로 지금 이 작업을 수행하지 않으면 등록을 제외한 모든 요청이 HTTP 코드 401을 반환합니다. 하지만 유효한 토큰이 있으면 /users/:userId 에서 다음 응답을 받습니다.

표시된 사용자에 대한 데이터를 나열하는 응답

또한 이전에 언급했듯이 교육 목적과 단순성을 위해 모든 필드를 표시합니다. 비밀번호(해시 또는 기타)는 응답에 표시되지 않아야 합니다.

사용자 목록을 가져오도록 합시다.

모든 사용자 목록 요청

놀라다! 403 응답을 받습니다.

적절한 권한 수준이 없기 때문에 작업이 거부되었습니다.

사용자는 이 끝점에 액세스할 수 있는 권한이 없습니다. 사용자의 permissionLevel 을 1에서 7로 변경해야 합니다(무료 및 유료 권한 수준이 각각 1과 4로 표시되기 때문에 5도 변경 가능). 대화형 프롬프트에서 MongoDB에서 수동으로 이 작업을 수행할 수 있습니다. , 다음과 같이(ID가 로컬 결과로 변경됨):

 db.users.update({"_id" : ObjectId("5b02c5c84817bf28049e58a3")},{$set:{"permissionLevel":5}})

그런 다음 새 JWT를 생성해야 합니다.

완료되면 적절한 응답을 얻습니다.

모든 사용자 및 해당 데이터에 대한 응답

다음으로 일부 필드가 포함된 PATCH 요청을 /users/:userId 엔드포인트로 전송하여 업데이트 기능을 테스트해 보겠습니다.

업데이트할 부분 데이터가 포함된 요청

성공적인 작업의 확인으로 204 응답을 예상하지만 사용자에게 다시 한 번 확인을 요청할 수 있습니다.

성공적인 변경 후 응답

마지막으로 사용자를 삭제해야 합니다. 위에서 설명한 대로 새 사용자를 만들고(사용자 ID를 기록해 두는 것을 잊지 마십시오) 관리자에게 적절한 JWT가 있는지 확인해야 합니다. 새 사용자는 삭제 작업도 수행할 수 있도록 권한을 2053으로 설정해야 합니다(2048— ADMIN — 이전 5 추가). 완료되고 새 JWT가 생성되면 Authorization 요청 헤더를 업데이트해야 합니다.

사용자 삭제 설정 요청

/users/:userIdDELETE 요청을 보내면 확인으로 204 응답을 받아야 합니다. 모든 기존 사용자를 나열하도록 /users/ 를 요청하여 다시 확인할 수 있습니다.

REST API의 다음 단계

이 자습서에서 다루는 도구와 방법을 사용하여 이제 Node.js에서 간단하고 안전한 REST API를 만들 수 있습니다. 프로세스에 필수적이지 않은 많은 모범 사례를 건너뛰었으므로 다음 사항을 잊지 마십시오.

  • 적절한 유효성 검사 구현(예: 사용자 이메일이 고유한지 확인)
  • 단위 테스트 및 오류 보고 구현
  • 사용자가 자신의 권한 수준을 변경하지 못하도록 방지
  • 관리자가 자신을 제거하지 못하도록 방지
  • 민감한 정보의 공개 방지(예: 해시된 암호)
  • JWT 비밀을 common/config/env.config.js 에서 오프-리포지토리, 비환경 기반 비밀 배포 메커니즘으로 이동합니다.

독자를 위한 마지막 연습은 JavaScript Promise 사용에서 async/await 기술로 코드베이스를 변환하는 것입니다.

관심이 있는 분들을 위해 이제 TypeScript 버전의 프로젝트도 사용할 수 있습니다.

관련 항목: REST 사양으로 해본 적이 없는 5가지