Nestjs 튜토리얼 따라하기 8편

Nestjs 튜토리얼 따라하기 8편

Guards

Guards

  • Guard는 @Injectable() 데코레이터를 사용하는 클래스이며 CanActivate 인터페이스를 구현하여 정의 된다.

  • Guard는 권한부여에 대한 결과를 처리한다. 이는 일반적으로 express의 미들웨어에 의해 처리 되었다.

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 를 갖는다. ExecutionContextArgumentHost`로 부터 상속된다.

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);
  }
}

Nestjs 튜토리얼 따라하기 7편

Nestjs 튜토리얼 따라하기 7편

Pipes

Pipes

  • Pipe 는 @Injectable() 데코레이터를 쓰는 클래스로서 PipeTransform 인터페이스를 구현한 클래스

  • Pipe에는 두가지 일반적인 사용법이 있다.

    1. 입력 데이터를 원하는 형식으로 변환
    2. 입력 데이터가 유효한지 확인

이 두 경우 모두 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() 함수에서 idParseIntPipe로 변환하여 사용한다. 만약 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는 현재 처리되고 있는 파라미터이며 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 {}

[C++] AtCoder Beginner Contest 213

AtCoder Beginner Contest 213

결과

submission rating

Rating : 701 -> 740 (+39)

풀이

A

a

A - Bitwise Exclusive Or

정수 A,B 가 있을 때 A ^ C = B 가 되는 C를 구하는 문제

양변에 A를 xor 해주면 A ^ B = C 가 되므로 이를 출력하면 정답

#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://atcoder.jp/contests/abc213/tasks/abc213_a
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    ll a,b;
    cin >> a >> b;
    ll ans = a ^ b;
    cout << ans;

    return 0;
}
B

b

B - Booby Prize

N명의 플레이어 들이 A_i 만큼의 점수를 가지고 있고 낮은 점수가 높은 순위를 기록한다고 했을 때

뒤에서 2번째로 낮은 순위의 플레이어를 찾는 문제

점수와 몇번째 플레이어인지를 vector에 기록한 뒤 이를 Sorting 하여 뒤에서 두번째값의 플레이어 번호를 출력하면 정답

#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://atcoder.jp/contests/abc213/tasks/abc213_b
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    ll n;
    cin >> n;
    vector<pair<ll,ll>> v;
    rep(i,n){
        ll a;
        cin >> a;
        v.push_back({a,i+1});
    }
    sort(all(v));
    for(ll i = 0;i<n;i++){
        if(i == n-2){
            cout << v[i].second;
        }
    }

    return 0;
}
C

c

C - Reorder Cards

높이 H, 너비 W의 행렬이 있을 때 (A_i, B_i)의 위치에 정수 i가 쓰여져 있다.

아무것도 쓰여있지 않은 행과 열은 지워진다고 했을 때 마지막 행렬에서의 i가 쓰여있는 좌표를 순서대로 출력하는 문

HW 크기의 행렬을 모두 검사하여 체크하기엔 H,W 값이 10^9승 까지이기 때문에 당연히 TLE가 발생한다.

문제를 잘 살펴보면 입력받은 각각의 A_i, B_i 배열에서 오름차순으로 배열했을때 순서를 출력한다고 보면 된다.

즉 (A_i, B_i) 입력이 다음과 같이 주어졌을 때

2 4
5 6
3 4

출력은 다음과 같다

1 1
3 2
2 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://atcoder.jp/contests/abc213/tasks/abc213_c
bool sortbysec(const tl3& a, const tl3& b) { return (get<1>(a) < get<1>(b)); }
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    ll h, w, n;
    cin >> h >> w >> n;
    vector<tl3> a;
    vector<tl3> b;
    for (ll i = 1; i <= n; i++) {
        ll ta, tb;
        cin >> ta >> tb;
        a.push_back(make_tuple(ta, i, 0));
        b.push_back(make_tuple(tb, i, 0));
    }
    sort(all(a));
    sort(all(b));
    ll rep = 0;
    for(ll i = 0;i<n;i++) {
        if(i == 0) a[i] = make_tuple(get<0>(a[i]), get<1>(a[i]), i+1);
        else if(i != 0 && get<0>(a[i]) == get<0>(a[i-1])) {
            a[i] = make_tuple(get<0>(a[i]), get<1>(a[i]), get<2>(a[i-1]));
            rep++;
        }
        else a[i] = make_tuple(get<0>(a[i]), get<1>(a[i]), i+1-rep);  
    }
    rep = 0;
    for(ll i = 0;i<n;i++) {
        if(i == 0) b[i] = make_tuple(get<0>(b[i]), get<1>(b[i]), i+1);
        else if(i != 0 && get<0>(b[i]) == get<0>(b[i-1])) {
            b[i] = make_tuple(get<0>(b[i]), get<1>(b[i]), get<2>(b[i-1]));
            rep++;
        }
        else b[i] = make_tuple(get<0>(b[i]), get<1>(b[i]), i+1-rep);  
    }
    sort(all(a), sortbysec);
    sort(all(b), sortbysec);
    rep(i, n) { cout << get<2>(a[i]) << " " << get<2>(b[i]) << "\n"; }

    return 0;
}
D

d

D - Takahashi Tour

N개의 도시가 있으며 N-1개의 경로를 입력받는다.

타카하시가 도시 1에서부터 출발할 때 아래와 같은 규칙으로 출력이 발생한다.

  • 타카하시가 현재 위치한 도시에서 바로 접근할 수 있는 도시 중 방문하지 않은 도시가 있다면 작은 숫자의 도시부터 방문
  • 그렇지 않으면 , 타카하시가 현재 위치한 도시가 1이라면 끝
  • 1이 아니라면 이전에 있던 도시로 되돌아옴

위 조건은 평범한 DFS임을 알 수 있다. 하지만 입력받은 경로를 sorting하는 과정이 필요하다.

자세한 해답은 아래 코드 참고

#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://atcoder.jp/contests/abc213/tasks/abc213_d
vll path[202020];
bool isVisited[202020] = {0};
void dfs(ll start) {
    isVisited[start] = true;
    cout << start << " ";
    for (auto k : path[start]) {
        if (!isVisited[k]) {
            isVisited[k] = true;
            dfs(k);
            cout << start << " ";
        }
    }
}
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    ll n;
    cin >> n;
    rep(i, n - 1) {
        ll a, b;
        cin >> a >> b;
        path[a].push_back(b);
        path[b].push_back(a);
    }
    for (ll i = 0; i <= n; i++) {
        sort(all(path[i]));
    }
    dfs(1);

    return 0;
}

[C++] AtCoder Beginner Contest 198

AtCoder Beginner Contest 198

풀이

A

A - Div

정수 N을 입력받았을 때

두 소년이 N개의 사탕을 분배할 수 있는 방법은 총 몇가지인지 묻는 문제

ex) 2명이 3개의 사탕을 분배하는 경우 -> (1,2) (2,1) 총 2가지 2명이 4개의 사탕을 분배하는 경우 -> (1,3) (2,2) (3,1) 총 3가지 …

N-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 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://atcoder.jp/contests/abc198/tasks/abc198_a
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    ll n;
    cin >> n;
    cout << n-1;

    return 0;
}
B

B - Palindrome with leading zeros

Leading Zero를 붙이거나 안붙였을때 펠린드롬이 되는지 여부를 확인하면 되는 문제

Leading Zero를 포함하는 펠린드롬이라면 원래 뒤에 0이 붙어있을 것이기 때문에 10으로 나눠질때 까지 나눠준 후 펠린드롬 여부를 확인하면 된다.

#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 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://atcoder.jp/contests/abc198/tasks/abc198_b
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    ll n;
    cin >> n;
    if(n == 0) {
        cout<<"Yes";
        return 0;
    }
    else {
        while(n%10 == 0)
            n/=10;
    }
    string s = to_string(n);
    string ss = s;
    reverse(all(s));
  
    if(ss == s) cout<<"Yes";
    else cout<<"No";

    return 0;
}
C

C - Compass Walking

R, X, Y를 입력받을 때 평면에서 한번에 R만큼의 거리를 갈 수 있다고 하자

(0,0)부터 시작해서 (X,Y)까지 도착하려면 총 몇번을 가야하는지 묻는 문제

도착 지점과 원점과의 거리가 R과 같다면 1 R 초과 2R 이하라면 2 이외의 경우에는 ceil(d/R)을 출력하면 정답

문제 이름대로 좌표평면에 원을 그려보면 쉽게 이해 가능

ss

#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 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://atcoder.jp/contests/abc198/tasks/abc198_c
int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    ll r,x,y;
    cin >> r >> x >> y;
    ll len = x*x + y*y;
    ll ans = 0;
    if(len == r*r) cout << 1;
    else if(len < r*r) cout << 2;
    else{
        while(r*r*ans*ans<len){
            ans++;
        }
        cout << ans;
    }

    return 0;
}

[C++] AtCoder Beginner Contest 197

AtCoder Beginner Contest 197

풀이

A

A - Rotate

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 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://atcoder.jp/contests/abc197/tasks/abc197_a
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    string s;
    cin >> s;
    for(int i = 1;i<s.length();i++){
        cout << s[i];
    }
    cout << s[0];

    return 0;
}
B

B - Visibility

정해진 맵이 H,W 크기로 있다고 할 때 맵 상에서 가로와 세로 일직선으로 쭉 볼 수 있다고 하자

(X,Y) 좌표가 보이는 좌표는 총 몇 군데 인지 계산하는 문제

(X,Y)를 기준으로 상하좌우로 반복문을 돌려 장애물이 나올때 까지 세면 정답

#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 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://atcoder.jp/contests/abc197/tasks/abc197_b
ll h, w, x, y;
vector<string> s;
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    cin >> h >> w >> x >> y;
    s.resize(h);
    rep(i, h) { cin >> s[i]; }
    ll ans = -3;
    x--;
    y--;
    for (ll i = x; i < h && s[i][y] != '#'; i++) ans++;
    for (ll i = x; i >= 0 && s[i][y] != '#'; i--) ans++;
    for (ll j = y; j < w && s[x][j] != '#'; j++) ans++;
    for (ll j = y; j >= 0 && s[x][j] != '#'; j--) ans++;

    cout << ans;

    return 0;
}
C

C - ORXOR

N개의 수를 입력받아 이를 두 그룹으로 나누어 한 그룹은 OR 나머지 그룹은 XOR을 한다고 할 때 최소값을 찾는 문제

N이 최대 20으로 작기 떄문에 모든 경우를 포함해도 시간내로 통과 가능

  
#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 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://atcoder.jp/contests/abc194/tasks/abc197_c
int main() {
    ios_base::sync_with_stdio(0);
    cin.tie(0);

    ll n;
    cin >> n;
    vll a(n);
    rep(i, n) { cin >> a[i]; }
    ll ans = INT_MAX;
    for (int i = 0; i < 1 << (n - 1); i++) {
        ll xor_ = 0;
        ll or_ = 0;
        for (int j = 0; j <= n; j++) {
            if (j < n) {
                or_ |= a[j];
            }
            if (j == n || (i >> j & 1)) {
                xor_ ^= or_ ;
                or_ = 0;
            }
        }
        ans = min(ans, xor_);
    }
    cout << ans;
    return 0;
}