19 Aug 2021 |
PS
Codeforces Round #739 (Div. 3)
결과

Rating : 1272 -> 1239 (-33)
풀이
A

A - Dislike of Threes
3의 배수 혹은 마지막 자리 수가 3인 수를 제외한 수 중 k번째 수가 어떤 수 인지 출력하는 문제
k가 최대 1000으로 수가 작기 떄문에 1000번째 수 까지 모든 경우를 저장하고 있다가 출력하면 정답
#pragma warning(disable : 4996)
#include <bits/stdc++.h>
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vll;
typedef vector<ld> vld;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
typedef tuple<ll, ll, ll> tl3;
#define FOR(a, b, c) for (int(a) = (b); (a) < (c); ++(a))
#define FORN(a, b, c) for (int(a) = (b); (a) <= (c); ++(a))
#define rep(i, n) FOR(i, 0, n)
#define repn(i, n) FORN(i, 1, n)
#define tc(t) while (t--)
// https://codeforces.com/contest/1560/problem/0
bool is_pass(ll n){
if(n % 3 == 0) return true;
if(n % 10 == 3) return true;
return false;
}
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
ll n;
cin >> n;
ll a[1001] = {0};
a[1] = 1;
for(int i = 2;i<=1000;i++){
ll val = a[i-1];
val++;
while(is_pass(val)) val++;
a[i] = val;
}
tc(n){
ll x;
cin >> x;
cout << a[x] << "\n";
}
return 0;
}
B

B - Who’s Opposite?
a,b,c 세 정수를 입력으로 받는다. a와 b는 원탁에서 정 반대의 위치에 있다고 할 때 c의 정 반대의 위치에 있는 수가 무엇인지 찾는 문제
우선 a와 b사이에 몇개의 수가 존재하는지 구한다. 그것을 토대로 원탁에 최대 몇 까지의 수가 있는지 구한다.
그렇게 되면 자연스럽게 c의 반대쪽에 있는 수를 알 수 있다.
하지만 답을 구할 수 없는 경우가 있는데
ll s = max(a, b);
ll e = min(a, b);
ll dif = s - e - 1;
ll end = s + dif - e + 1;
를 토대로 a와 b사이의 수 dif개가 있고 원탁에 있는 최대 수는 end 라고 했을 떄 만약 end가 a혹은 b보다 크다면 그것은 답이 존재하지 않는다.
혹은 c가 end 보다 크면 c는 원탁에 존재할 수 없는 수 이므로 그 경우 또한 답이 존재하지 않는다.
그리고 a와 b사이에 수가 dif개가 있는데 어떤 경우라도 1이 들어갈 자리가 없다면 그 경우도 답이 존재하지 않는다.
이러한 경우를 제외한 모든 경우에는 답이 존재하게 된다.
#pragma warning(disable : 4996)
#include <bits/stdc++.h>
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vll;
typedef vector<ld> vld;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
typedef tuple<ll, ll, ll> tl3;
#define FOR(a, b, c) for (int(a) = (b); (a) < (c); ++(a))
#define FORN(a, b, c) for (int(a) = (b); (a) <= (c); ++(a))
#define rep(i, n) FOR(i, 0, n)
#define repn(i, n) FORN(i, 1, n)
#define tc(t) while (t--)
// https://codeforces.com/contest/1560/problem/B
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
ll n;
cin >> n;
tc(n) {
ll a, b, c;
cin >> a >> b >> c;
ll s = max(a, b);
ll e = min(a, b);
ll dif = s - e - 1;
ll end = s + dif - e + 1;
if (end < s || end < e || end < c) {
cout << -1 << "\n";
continue;
}
if ((e + dif + 1) % end != s % end) {
cout << -1 << "\n";
continue;
}
ll ans = (c + end / 2) % end;
if (ans == 0) ans = end;
cout << ans << "\n";
}
return 0;
}
C

C - Infinity Table
2차원 행렬에서 정수가 일정한 패턴대로 작성되어 있다. 그럼 x는 해당 행렬에 어느 좌표에 있을지를 구하는 문제
해당 규칙은 간단한 규칙이므로 쉽게 눈에 보일 것이다.
(1,1) -> (1,2) -> (2,2) -> (2,1) -> (1,3) -> …. 과 같은 순서로 진행된다.
정해진 규칙대로 입력받은 수가 있는 좌표를 구하면 정답
#pragma warning(disable : 4996)
#include <bits/stdc++.h>
#define all(x) (x).begin(), (x).end()
using namespace std;
typedef long long ll;
typedef long double ld;
typedef vector<ll> vll;
typedef vector<ld> vld;
typedef pair<ll, ll> pll;
typedef pair<ld, ld> pld;
typedef tuple<ll, ll, ll> tl3;
#define FOR(a, b, c) for (int(a) = (b); (a) < (c); ++(a))
#define FORN(a, b, c) for (int(a) = (b); (a) <= (c); ++(a))
#define rep(i, n) FOR(i, 0, n)
#define repn(i, n) FORN(i, 1, n)
#define tc(t) while (t--)
// https://codeforces.com/contest/1560/problem/C
int main() {
ios_base::sync_with_stdio(0);
cin.tie(0);
ll n;
cin >> n;
ll a[31624] = {0};
a[1] = 1;
a[2] = 2;
a[3] = 5;
ll add = 5;
for (int i = 4; i < 31624; i++) {
a[i] = a[i - 1] + add;
add += 2;
}
tc(n) {
ll x;
cin >> x;
if (x == 1) {
cout << "1 1\n";
continue;
}
ll start = 0;
for (int i = 1;; i++) {
if (a[i] <= x && a[i + 1] > x) {
start = i;
break;
}
}
if (x < a[start] + start - 1) {
cout << x - a[start] + 1 << " " << start << "\n";
} else if (x == a[start] + start - 1) {
cout << start << " " << start << "\n";
} else {
cout << start << " " << start - x + a[start] + start - 1 << "\n";
}
}
return 0;
}
16 Aug 2021 |
Tutorial

commitlint
는 commit message를 linting해주는 모듈입니다. 이를 husky
혹은 github action과
함께 사용한다면 더욱 편리하게 commit message를 관리할 수 있습니다.
https://commitlint.js.org/#/
설치
yarn add @commitlint/cli @commitlint/config-conventional -D
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
Husky를 이용한 Commitlint 적용법
"husky": {
"hooks": {
"commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
}
}
위와 같이 husky를 설정하게 되면 git commit
수행 시 커밋 메시지를 불러와 commitlint를 수행하게 됩니다.
"husky": {
"hooks": {
"commit-msg": "if git-branch-is dev; then commitlint -E HUSKY_GIT_PARAMS; fi"
}
},
또한 git-branch-is
와 결합하여 특정 브랜치에서만 commitlint가 수행되게 할 수도 있습니다.
위 명령어는 dev
브랜치에 대해서만 commitlint
를 수행하게 하는 명령어 입니다.
Github Workflow를 이용한 Commitlint 적용법
Github Action을
사용하여 commitlint
를 수행할 수 있습니다.
이러한 Workflow를 사용하여 PR시 commit convention을 준수하지 않은 PR에 대해 merge를 막는다거나 하는 방법으로 사용할 수 있습니다.
# Run commitlint on Pull Requests and commits
name: commitlint
on:
pull_request:
types: ['opened', 'edited', 'reopened', 'synchronize']
push:
branches:
- '**'
jobs:
lint-pull-request-name:
# Only on pull requests
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- run: yarn add @commitlint/config-conventional
- uses: JulienKode/pull-request-name-linter-action@v0.1.2
lint-commits:
# Only if we are pushing or merging PR to the master
if: (github.event_name == 'pull_request' && github.base_ref == 'refs/heads/master') || github.event_name == 'push'
runs-on: ubuntu-latest
env:
GITHUB_TOKEN: $
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 30 # Its fine to lint last 30 commits only
- run: yarn add @commitlint/config-conventional
- uses: wagoid/commitlint-github-action@v1
15 Aug 2021 |
Backend
Nestjs 튜토리얼 따라하기 9편
Interceptors

Aspect interception
아래는 간단한 LoggingInterceptor
예제이다.
import { Injectable, NestInterceptor, ExecutionContext, CallHandler } from '@nestjs/common';
import { Observable } from 'rxjs';
import { tap } from 'rxjs/operators';
@Injectable()
export class LoggingInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
console.log('Before...');
const now = Date.now();
return next
.handle()
.pipe(
tap(() => console.log(`After... ${Date.now() - now}ms`)),
);
}
}
Binding interceptors
Interceptor를 적용하기 위하여 @UseInterceptors()
데코레이터를 사용할 것이다. Interceptor도 controller에 적용할 수 있고 혹은 전역으로 적용할 수 있다.
@UseInterceptors(LoggingInterceptor)
export class CatsController {}
전역으로 적용하려면 아래와 같이 구성한다.
const app = await NestFactory.create(AppModule);
app.useGlobalInterceptors(new LoggingInterceptor());
import { Module } from '@nestjs/common';
import { APP_INTERCEPTOR } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_INTERCEPTOR,
useClass: LoggingInterceptor,
},
],
})
export class AppModule {}
15 Aug 2021 |
Backend
Nestjs 튜토리얼 따라하기 8편
Guards

express의 미들웨어는 next()를 호출하게 되면 그 이후에 무슨 함수가 호출될 지 모르지만 Guards는 ExecutionContext
에 접근할 수 있으므로 다음에 실행될 항목을 정확하게 알 수 있다.
Authorization guard
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
const request = context.switchToHttp().getRequest();
return validateRequest(request);
}
}
Execution context
```canActive() 함수는 파라미터로
ExecutionContext 를 갖는다.
ExecutionContext는
ArgumentHost`로 부터 상속된다.
Role-based authentication
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Observable } from 'rxjs';
@Injectable()
export class RolesGuard implements CanActivate {
canActivate(
context: ExecutionContext,
): boolean | Promise<boolean> | Observable<boolean> {
return true;
}
}
Guard 적용하기
Pipe처럼 Guard도 Controller에 적용하거나, 전역으로 적용할 수 있다. Controller에 적용하기 위해서 @UseGuards()
데코레이터를 사용해야 한다.
@Controller('cats')
@UseGuards(RolesGuard)
export class CatsController {}
전역으로 적용하기 위해서 useGlobalGuards()
메소드를 사용한다.
const app = await NestFactory.create(AppModule);
app.useGlobalGuards(new RolesGuard());
import { Module } from '@nestjs/common';
import { APP_GUARD } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_GUARD,
useClass: RolesGuard,
},
],
})
export class AppModule {}
Setting roles per handler
위에서 구현한 RolesGuard
가 잘 작동하기 위해서 각 Controller Route마다 어떤 권한을 가지고 있을 때 권한을 허가할건지 설정해야한다.
@Post()
@SetMetadata('roles', ['admin'])
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
혹은 직접 데코레이터를 생성해서 권한을 설정할 수 있다.
import { SetMetadata } from '@nestjs/common';
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
@Post()
@Roles('admin')
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
위에서 구현한 것을 모두 합치면
import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
@Injectable()
export class RolesGuard implements CanActivate {
constructor(private reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const roles = this.reflector.get<string[]>('roles', context.getHandler());
if (!roles) {
return true;
}
const request = context.switchToHttp().getRequest();
const user = request.user;
return matchRoles(roles, user.roles);
}
}
15 Aug 2021 |
Backend
Nestjs 튜토리얼 따라하기 7편
Pipes

이 두 경우 모두 Pipe는 Controller route handler 에서 작동한다.
Nest에는 내장 Pipe가 구현되어 있다. 또한 Custom Pipe도 구현할 수 있다.
내장 Pipe
- ValidationPipe
- ParseIntPipe
- ParseFloatPipe
- ParseBoolPipe
- ParseArrayPipe
- ParseUUIDPipe
- ParseEnumPipe
- DefaultValuePipe
모든 내장 Pipe들은 @nestjs/common
패키지에서 import 된다.
Pipe 활용
Pipe를 사용하기 위해서 다음과 같이 사용된다.
@Get(':id')
async findOne(@Param('id', ParseIntPipe) id: number) {
return this.catsService.findOne(id);
}
findOne()
함수에서 id
를 ParseIntPipe
로 변환하여 사용한다. 만약 id
가 number 타입이 아니라면 아래와 같은 에러를 발생시킨다.
{
"statusCode": 400,
"message": "Validation failed (numeric string is expected)",
"error": "Bad Request"
}
아래와 같이 statusCode를 변경하는것도 가능하다.
@Get(':id')
async findOne(
@Param('id', new ParseIntPipe({ errorHttpStatusCode: HttpStatus.NOT_ACCEPTABLE }))
id: number,
) {
return this.catsService.findOne(id);
}
Custom Pipe
위에서 언급했듯이 Custom Pipe를 생성할 수 있다.
import { PipeTransform, Injectable, ArgumentMetadata } from '@nestjs/common';
@Injectable()
export class ValidationPipe implements PipeTransform {
transform(value: any, metadata: ArgumentMetadata) {
return value;
}
}
모든 Pipe는 transform() 메소드를 구현해야 한다. 이를 위해서 2개의 파라미터가 필요하다
value는 현재 처리되고 있는 파라미터이며 metadata는 value의 메타데이터 이다.
export interface ArgumentMetadata {
type: 'body' | 'query' | 'param' | 'custom';
metatype?: Type<unknown>;
data?: string;
}
Schema 기반의 유효성 검사
@Post()
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
export class CreateCatDto {
name: string;
age: number;
breed: string;
}
위와 같은 create()
메소드와 CreateCatDto
가 있을 때 유효성을 검사할 수 있도록 Pipe를 수정해 보도록 하겠다.
오브젝트 Schema 유효성 검사
여기서는 joi를 기반의 스키마를 사용하는 유효성 검사 Pipe를 구현한다.
먼저 아래의 패키지를 설치한다
$ npm install --save joi
$ npm install --save-dev @types/joi
import { PipeTransform, Injectable, ArgumentMetadata, BadRequestException } from '@nestjs/common';
import { ObjectSchema } from 'joi';
@Injectable()
export class JoiValidationPipe implements PipeTransform {
constructor(private schema: ObjectSchema) {}
transform(value: any, metadata: ArgumentMetadata) {
const { error } = this.schema.validate(value);
if (error) {
throw new BadRequestException('Validation failed');
}
return value;
}
}
Validation Pipe 적용
위에서 만든 JoiValidationPipe
를 적용하기 위해서 아래와 같은 과정을 따른다.
JoiValidationPipe
인스턴스를 생성한다.
- joi schema를 생성자의 파라미터로 넣는다.
- 메소드에 해당 pipe를 바인딩한다.
이를 수행하면 아래와 같은 method처럼 구성된다.
@Post()
@UsePipes(new JoiValidationPipe(createCatSchema))
async create(@Body() createCatDto: CreateCatDto) {
this.catsService.create(createCatDto);
}
Class validator
Class validator를 사용하기 위하여 아래의 패키지를 설치한다.
$ npm i --save class-validator class-transformer
위 패키지가 설치되면 CreateCatDto
클래스에 몇가지 데코레이터를 추가할 수 있다.
import { IsString, IsInt } from 'class-validator';
export class CreateCatDto {
@IsString()
name: string;
@IsInt()
age: number;
@IsString()
breed: string;
}
이를 사용하게 되면 별도의 유효성 검사를 위한 클래스를 만들 필요가 없다.
Global scoped pipes
ValidationPipe
를 일반화 시켜 만들고 이를 전역으로 선언하고 싶다면 아래와 같이 구성한다.
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(new ValidationPipe());
await app.listen(3000);
}
bootstrap();
import { Module } from '@nestjs/common';
import { APP_PIPE } from '@nestjs/core';
@Module({
providers: [
{
provide: APP_PIPE,
useClass: ValidationPipe,
},
],
})
export class AppModule {}