window.pipedriveLeadboosterConfig = { base: 'leadbooster-chat.pipedrive.com', companyId: 11580370, playbookUuid: '22236db1-6d50-40c4-b48f-8b11262155be', version: 2, } ;(function () { var w = window if (w.LeadBooster) { console.warn('LeadBooster가 이미 존재합니다') } else { w.LeadBooster = { q: [], on: 함수 (n, h) { this.q.push({ t: 'o', n: n, h: h }) }, trigger: 함수 (n) { this.q.push({ t: 't', n: n }) }, } } })() Netlify 함수를 사용하여 GraphQL/MongoDB API 배포 - The Codest
The Codest
  • 회사 소개
  • 서비스
    • 소프트웨어 개발
      • 프론트엔드 개발
      • 백엔드 개발
    • Staff Augmentation
      • 프론트엔드 개발자
      • 백엔드 개발자
      • 데이터 엔지니어
      • 클라우드 엔지니어
      • QA 엔지니어
      • 기타
    • IT 자문
      • 감사 및 컨설팅
  • 산업 분야
    • 핀테크 및 뱅킹
    • E-commerce
    • 애드테크
    • 헬스 테크
    • 제조
    • 물류
    • 자동차
    • IOT
  • 가치
    • CEO
    • CTO
    • 배달 관리자
  • 우리 팀
  • Case Studies
  • 방법 알아보기
    • 블로그
    • 모임
    • 웹 세미나
    • 리소스
채용 정보 연락하기
  • 회사 소개
  • 서비스
    • 소프트웨어 개발
      • 프론트엔드 개발
      • 백엔드 개발
    • Staff Augmentation
      • 프론트엔드 개발자
      • 백엔드 개발자
      • 데이터 엔지니어
      • 클라우드 엔지니어
      • QA 엔지니어
      • 기타
    • IT 자문
      • 감사 및 컨설팅
  • 가치
    • CEO
    • CTO
    • 배달 관리자
  • 우리 팀
  • Case Studies
  • 방법 알아보기
    • 블로그
    • 모임
    • 웹 세미나
    • 리소스
채용 정보 연락하기
뒤로 화살표 뒤로 가기
2021-05-13
소프트웨어 개발

Netlify 함수를 사용하여 GraphQL/MongoDB API 배포하기

The Codest

파웰 리브친스키

Software Engineer

목표 초기 설정 종속성 설치 시작하기 먼저 메인 디렉터리에 tsconfig.json을 추가합니다: 이제 서버 구현을 위한 src/server.ts를 만들어 보겠습니다. 그런 다음 로컬 서버용 함수와 람다용 함수 두 개를 추가합니다. 이제 리졸버나 타입 정의가 없으므로 몇 가지를 만들어야 합니다. 처음에는 [...]를 원한다고 가정해 봅시다.

목표

  1. 로컬 서버와 람다 서버를 모두 구성합니다.
  2. 둘 다 MongoDB에 연결합니다.
  3. 기본 인증을 구현합니다.
  4. 서버리스 Apollo 배포 GraphQL API를 사용하세요.
  5. 타입스크립트 사용.

초기 설정

npm init -y

설치 종속성

npm install --save typescript graphql aws-lambda @types/aws-lambda

시작하기

먼저 tsconfig.json 를 기본 디렉토리에 추가합니다:

 {
 "컴파일러옵션": {
 "target": "es5",
 "module": "commonjs",
 "allowJs": true,
 "strict": true,
 "esModuleInterop": true,
 "skipLibCheck": true,
 "forceConsistentCasingInFileNames": true
 },
 "include": ["src/*.ts", "src/**/*.ts", "src/**/*.js"],
 "exclude": ["node_modules"]
 }

이제 다음을 만들어 보겠습니다. src/server.ts 를 서버 구현에 추가합니다. 그런 다음 로컬 서버용 함수와 람다용 함수 두 개를 추가합니다.

// src/server.ts
"apollo-server-lambda"에서 { ApolloServer를 ApolloServerLambda로 }를 가져옵니다;
"apollo-server"에서 { ApolloServer }를 가져옵니다;


const createLambdaServer = () => =>.
  new ApolloServerLambda({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    },
  });

const createLocalServer = () => =>.
  new ApolloServer({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    },
  });

export { createLambdaServer, createLocalServer };

리졸버나 유형 정의가 없으므로 몇 가지를 만들어야 합니다. 먼저 사용자를 생성하고 사용자에 대한 정보를 수신한다고 가정해 보겠습니다.

// src/schemas.ts
const { gql } = require("apollo-server-lambda");

const userSchema = gql`
  유형 사용자 {
    id: ID!
    email: String!
    name: 문자열!
  }

  유형 Query {
    user(id: ID!): User!
  }

  유형 Mutation {
    createUser(이름: 문자열!, 이메일: 문자열!, 비밀번호: 문자열!): User!
  }
`;

이 기능이 익숙하지 않으시다면 아폴로는 아주 멋진 튜토리얼을 준비했습니다.

이제 하나의 쿼리와 하나의 변형을 가진 사용자 확인자를 만들어 보겠습니다.

// src/resolvers.ts
const userResolver = {
  Query: {
    user: async (parent, args, context, info) => {
      {...}
    },
  },
  Mutation: {
    createUser: async (parent, args, context, info) => {
      {...}
    },
  },
};

하지만 데이터가 없네요... 해결해 봅시다 😉

유형 정의와 리졸버를 서버로 가져오는 것을 잊지 마세요.

// src/server.ts
"apollo-server-lambda"에서 { ApolloServer를 ApolloServerLambda로 }를 가져옵니다;
"apollo-server"에서 { ApolloServer }를 가져옵니다;

"./schemas"에서 { typeDefs }를 가져옵니다;
"./resolvers"에서 { resolvers }를 가져옵니다;

{...}

몽구스를 통해 MongoDB와 연결하기

이제 데이터베이스와 연결할 수 있는 좋은 시기입니다. 이 특별한 경우에는 MongoDB를 사용하겠습니다. 무료이며 유지 관리가 쉽습니다. 하지만 그 전에 두 가지 종속성을 더 설치해 보겠습니다:

npm 설치 --save 몽구스 닷텐브 저장

첫 번째 단계는 사용자 모델을 만드는 것입니다.

// src/model.ts
"몽구스"에서 mongoose, { 문서, 오류, 스키마 }를 가져옵니다;

내보내기 유형 사용자 = 문서 & {
  _id: 문자열,
  이메일: 문자열
  이름: 문자열
  password: 문자열
};

삭제 몽구스.연결.모델["사용자"];

const UserSchema: Schema = new Schema({
  email: {
    type: String,
    required: true,
    unique: true,
  },
  이름: {
    type: 문자열,
    required: true,
    minLength: 3,
    maxLength: 32,
  },
  password: {
    type: 문자열,
    required: true,
  },
});

export const userModel = mongoose.model  ("User", UserSchema);

더 안전한 비밀번호 만들기

보안이 우선입니다! 이제 비밀번호를 해싱하여 보안을 강화해 보겠습니다.

npm install --save bcrypt @types/bcrypt

이제 미들웨어 전 콜백 내에서 비밀번호 보안을 구현합니다. 사전 미들웨어 함수는 각 미들웨어가 다음 미들웨어를 호출할 때 차례로 실행됩니다. 비밀번호를 안전하게 보호하기 위해 별도의 함수 호출에 대해 솔트와 해시를 생성하는 기술을 사용하고 있습니다.

// src/model.ts
"bcrypt"에서 bcrypt를 임포트합니다;
{...}
const SALT_WORK_FACTOR: number = 10;

UserSchema.pre("save", function (next) { {
  const user = this as User;
  if (!this.isModified("password")) return next();

  bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
    if (err) return next(err);

    bcrypt.hash(user.password, salt, function (err, hash) {
      if (err) return next(err);
      user.password = hash;
      next();
    });
  });
});
{...}

그런 다음 UserSchema에 비교 비밀번호 메서드를 추가합니다:

// src/model.ts
{...}
UserSchema.methods.comparePasswords = 함수 (
  후보 비밀번호: 문자열
  cb: (err: 오류 | null, same: boolean | null) => void
) {
  const user = this as User;
  bcrypt.compare(candidatePassword, user.password, (err, isMatch) => {
    if (err) {
      반환 cb(err, null);
    }
    cb(null, isMatch);
  });
};
{...}

물론 사용자 유형도 수정합니다.

유형 비교 비밀번호 함수 = (
  후보 비밀번호: 문자열,
  cb: (err: 오류, isMatch: boolean) => void
) => void;

내보내기 유형 사용자 = 문서 & {
  _id: 문자열,
  이메일: 문자열
  이름: 문자열
  password: 문자열,
  비교 비밀번호: 비교 비밀번호 함수,
};

이제 서버와 데이터베이스 간의 연결을 준비할 수 있습니다. MONGODB_URI 는 연결을 만드는 데 필요한 연결 문자열이 포함된 환경 변수입니다. 몽고DB 아틀라스 계정에 로그인한 후 클러스터 패널에서 가져올 수 있습니다. 안에 넣기 .env

// .env
mongodb_uri = ...;

해당 파일을 항상 다음 위치에 추가하는 것을 잊지 마십시오. .gitignore. 훌륭합니다! 이제 DB와 연결할 수 있는 함수를 추가해 보겠습니다.

// src/server.ts
"몽구스"에서 몽구스, { 연결 }을 가져옵니다;
{...}
let cachedDb: Connection;

const connectToDatabase = async () => {
  if (cachedDb) return;

  await mongoose.connect(process.env.MONGODB_URI || "", {
    useNewUrlParser: true,
    useUnifiedTopology: true,
    useFindAndModify: false,
    useCreateIndex: true,
  });
  cachedDb = mongoose.connection;
};
{...}

컨텍스트는 모든 리졸버에서 공유되는 객체입니다. 이를 제공하려면 ApolloServer 생성자에 컨텍스트 초기화 함수를 추가하기만 하면 됩니다. 해봅시다.

// src/server.ts
"./models/user.model"에서 { userModel }을 임포트합니다;
{...}
const createLambdaServer = async () => =>.
  new ApolloServerLambda({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    context: async () => {
      await connectToDatabase();

      반환 {
        models: {
          userModel,
        },
      };
    },
  });

const createLocalServer = () => =>.
  new ApolloServer({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    context: async () => {
      await connectToDatabase();

      반환 {
        models: {
          userModel,
        },
      };
    }
  });

보시다시피, 저희는 또한 사용자 모델 통해 컨텍스트. 이제 리졸버를 업데이트할 수 있습니다:

// resolvers.ts
const userResolver = {
  Query: {
    user: async (_, { email, name, password }, { models: { userModel } }) => {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutation: {
    createUser: async (_, { id }, { models: { userModel } }) => {
      const user = await userModel.create({ 이메일, 이름, 비밀번호 });
      반환 사용자;
    },
  },
};

멋지네요! 이제 기본 서버 인스턴스를 생성합니다:

// src/index.ts
"./서버"에서 { createLocalServer }를 가져옵니다;
require("dotenv").config();

const port = process.env.PORT || 4000;

const server = createLocalServer();

server.listen(port).then(({ url }) => {
  console.log(`${url}에서 실행 중인 서버`);
});

노데몬으로 로컬 서버 시작하기

실행하기 전에 마지막으로 nodemon.json

{
  "watch": ["src"],
  "ext": ".ts,.js",
  "ignore": [],
  "exec": "ts-node --transpile-only ./src/index.ts"
}

다음에 스크립트 추가 package.json

"스크립트": {
    "start": "nodemon"
  },

그리고 달려!

npm 시작

터미널에서 이러한 정보를 얻을 수 있습니다:

http://localhost:4000/ 에서 실행 중인 서버

그렇다면 http://localhost:4000/ 를 클릭합니다.

GraphQL 플레이그라운드가 나타납니다. 새 사용자를 만들어 보겠습니다!

createUser.png

데이터베이스에서 어떻게 보이는지 살펴보세요.

databaseJohnDoe.png

멋지다! 모든 것이 잘 작동합니다!

사용자 정보를 가져와 보겠습니다.

userWithoutAuth.png

그리고 비밀번호 필드를 추가합니다...

userPassword.png

Nice! 오류가 발생했습니다. "사용자" 유형에서 "비밀번호" 필드를 쿼리할 수 없습니다.". 보시다시피 사용자 유형 정의 내에 이 필드를 추가하지 않았습니다. 일부러 넣은 것입니다. 비밀번호나 기타 민감한 데이터를 쿼리할 수 있도록 해서는 안 됩니다.

또 한 가지... 인증 없이 사용자 데이터를 얻을 수 있다는 것은 좋은 해결책이 아닙니다. 우리는 그것을 고쳐야 합니다.

하지만 그 전에는...

Codegen 구성

GraphQL을 사용해 보겠습니다. 코드 생성기를 사용하여 스키마에 따라 호환되는 기본 유형을 가져옵니다.

npm install --save @graphql-codegen/cli @graphql-codegen/introspection
그래프 작성 @그래프 작성 @그래프 작성 @그래프 작성 @그래프 작성 리졸버

만들기 codegen.yml

덮어쓰기: true
스키마: "http://localhost:4000"
생성합니다:
  ./src/generated/graphql.ts:
    플러그인
      - "typescript"
      - "typescript-resolvers"
  ./graphql.schema.json:
    플러그인
      - "introspection"

추가 코드젠 스크립트를 package.json

"스크립트": {
    "start": "노데몬",
    "codegen": "graphql-codegen --config ./codegen.yml",
  },

그런 다음 로컬 서버가 실행 중일 때 스크립트를 실행합니다:

npm 실행 코드젠

성공할 경우 메시지를 받게 됩니다:

  √ 구문 분석 구성
  √ 출력 생성

해당 정보를 받으면 두 개의 파일이 나타납니다:

  • graphql.schema.json 메인 디렉토리에
  • graphql.ts 새로 생성된 경로에 src/generated

저희는 두 번째에 더 관심이 있습니다. 열면 멋진 유형 구조를 확인할 수 있습니다.

이제 리졸버를 개선할 수 있습니다:

// src/resolvers.ts
"./generated/graphql"에서 { 해석기, 토큰, 사용자 }를 임포트합니다;

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): Promise => {
      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
  },
  Mutation: {
    createUser: async (
      _,
      { 이메일, 이름, 비밀번호 },
      { models: { userModel } }
    ): Promise => {
      const user = await userModel.create({
        이메일
        name,
        password,
      });
      반환 사용자;
    },
  },
};

인증

다음으로 간단한 토큰 기반 인증을 설정해 보겠습니다.

npm install --save jsonwebtoken @types/jsonwebtoken

만들기 checkAuth 함수를 호출하여 토큰이 유효한지 확인합니다. 결과를 컨텍스트에 추가하여 리졸버 내부에서 액세스할 수 있도록 합니다.

// src/server.ts
import { IncomingHttpHeaders } from "http";
import {
  APIGatewayProxyEvent,
  APIGatewayProxyEventHeaders,
  Context,
} from "aws-lambda";
import jwt from "jsonwebtoken";
{...}
const checkAuth = async ({ token } : APIGatewayProxyEventHeaders | IncomingHttpHeaders ) => {
  if (typeof token === "string") {
    try {
      return await jwt.verify(token, "riddlemethis");
    } catch (e) {
      throw new AuthenticationError(`Your session expired. Sign in again.`);
    }
  }
};
{...}
const createLambdaServer = async (
  { headers }: APIGatewayProxyEvent,
  context: Context
) => {
  return new ApolloServerLambda({
    typeDefs,
    resolvers,
    context: async () => {
      await connectToDatabase();

      const auth = await checkAuth(headers);

      return {
        auth,
        models: {
          userModel,
        },
      };
    },
  });
};

function createLocalServer() {
  return new ApolloServer({
    typeDefs,
    resolvers,
    introspection: true,
    playground: true,
    context: async ({ req: { headers } = {} }) => {
      const auth = await checkAuth(headers);
      await connectToDatabase();

      return {
         auth,
         models: {
          userModel,
        },
      };
    },
  });
}

또한 이러한 토큰을 생성하는 방법이 필요합니다. 가장 좋은 방법은 리졸버 내부에 로그인 쿼리를 구현하는 것입니다.

// resolvers.ts
"./generated/graphql"에서 { 해석기, 토큰, 사용자 }를 가져옵니다;

const userResolver: Resolvers = {
  Query: {
    user: async (_, { id }, { models: { userModel }, auth }): Promise => {
      if (!auth) throw new AuthenticationError("인증되지 않았습니다");

      const user = await userModel.findById({ _id: id }).exec();
      return user;
    },
    login: async (
      _,
      { 이메일, 비밀번호 },
      { models: { userModel } }
    ): Promise => {
      const user = await userModel.findOne({ email }).exec();

      if (!user) throw new AuthenticationError("유효하지 않은 자격 증명");

      const matchPasswords = bcrypt.compareSync(password, user.password);

      if (!matchPasswords) throw new AuthenticationError("잘못된 자격증명");

      const token = jwt.sign({ id: user.id }, "riddlemethis", {
        expiresIn: 60,
      });

      반환 { 토큰 };
    },
  },
  Mutation: {
    createUser: async (
      _,
      { 이메일, 이름, 비밀번호 },
      { models: { userModel } }
    ): Promise => {
      const user = await userModel.create({
        이메일
        name,
        password,
      });
      반환 사용자;
    },
  },
};

토큰 유형 및 로그인 쿼리별로 사용자 유형 정의도 업데이트해야 합니다.

유형 토큰 {
    토큰: 문자열!
  }

타입 쿼리 {
    user(id: ID!): User!
    login(이메일: 문자열!, 비밀번호: 문자열!): Token!
  }

이제 토큰 없이 사용자를 확보해 보겠습니다.

noAuth.png

좋아요, 잘 작동합니다! 로그인을 시도해 보세요.

토큰.png

이번에는 헤더에 토큰을 추가하여 사용자를 다시 가져와 보겠습니다.

withToken.png

멋지네요! 자격 증명을 잘못 설정하면 어떻게 되나요?

잘못된 이메일.png

멋지네요!

배포 준비

마지막 단계: Netlify로 서버리스 API 배포!

폴더 만들기 람다 를 메인 디렉토리에 넣고 그 안에 두 개의 파일을 넣습니다:

첫 번째에는 다음이 포함됩니다. AWS 핸들러를 생성합니다. ApolloServerLambda 서버 인스턴스를 생성하고
를 호출한 다음 해당 인스턴스의 createHandler를 사용하여 핸들러를 노출합니다.

// lambda/graphql.ts
"aws-lambda"에서 { APIGatewayProxyEvent, Context }를 가져옵니다;
"???"에서 { createLambdaServer }를 가져옵니다;

export const handler = async (
  event: APIGatewayProxyEvent,
  context: 컨텍스트
) => {
  const server = await createLambdaServer(event, context);

  return new Promise((res, rej) => {
    const cb = (err: 오류, args: any) => (err ? rej(err) : res(args));
    server.createHandler()(event, context, cb);
  });
};

자세한 내용은 여기에서 확인할 수 있습니다.

두 번째는 tsconfig입니다. 중요한 부분은 outDir 필드에 입력합니다.

// lambda/tsconfig.json
{
  "컴파일러옵션": {
    "sourceMap": true,
    "noImplicitAny": true,
    "module": "commonjs",
    "target": "es6",
    "experimentalDecorators": true,
    "allowSyntheticDefaultImports": true,
    "moduleResolution": "노드",
    "skipLibCheck": true,
    "esModuleInterop": true,
    "outDir": "./dist"
  },
  "include": ["./*.ts", "./**/*.ts", "./**/*.js"]
}

하지만 문제가 있습니다. 사용할 수 없습니다. /src dir로 이동해야 합니다. 왜냐하면 Netlify는 람다 폴더 외부에 도달할 수 없기 때문입니다. 따라서 번들로 묶어야 합니다.

npm 설치 --save ncp

디렉토리를 복사할 수 있는 패키지입니다.

추가 번들 스크립트를 package.json

"스크립트": {
    "번들": "ncp ./src ./lambda/bundle",
    "codegen": "graphql-codegen --config ./codegen.yml",
    "start": "nodemon",
  },

이제 다음을 실행하면 npm 실행 번들새로 생성된 람다/번들 디렉토리의 모든 파일이 있습니다. src/.

내부에서 가져오기 경로 업데이트 lambda/graphql.ts

// lambda/graphql.ts
"aws-lambda"에서 { APIGatewayProxyEvent, Context }를 가져옵니다;
"./bundle/server"에서 { createLambdaServer }를 가져옵니다;

{...}

이제 람다/번들 디렉터리를 다음 위치에 추가할 수 있습니다. .gitignore

Netlify로 배포

빌드 명령이 무엇인지, 함수가 어디에 있는지 Netlify에 알려줘야 합니다. 이를 위해 netlify.toml file:

// netlify.toml
[빌드]
  명령 = "npm 실행 빌드:람다"
  함수 = "lambda/dist"

보시다시피, 이 디렉토리는 outDir 필드에 lambda/tsconfig.json

앱 구조는 다음과 같아야 합니다(음... 일부분 ;)).

앱
└───람다
│ └────번들
│ │ graphql.ts
│ tsconfig.json.ts
└───src
│ └───-generated
│ │ │ graphql.ts
│ │ index.ts
│ │ model.ts
│ │ resolvers.ts
│ │ schemas.ts
│ server.ts
│
│ codegen.yml
│ graphql.schema.json
│ netlify.toml
│ nodemon.json
│ tsconfig.json

추가 번들:람다 스크립트를 package.json

"스크립트": {
    "빌드:람다": "npm 실행 번들 && tsc -p lambda/tsconfig.json",
    "번들": "ncp ./src ./lambda/bundle",
    "codegen": "graphql-codegen --config ./codegen.yml",
    "start": "nodemon",
  },

배포

Netlify를 통해 앱을 배포해 보겠습니다.

  1. Netlify 계정에 로그인합니다,
  2. Github에 연결하세요,
  3. 'Git에서 새 사이트 만들기' 버튼을 클릭합니다,
  4. 적절한 리포지토리를 선택합니다,
  5. 빌드 명령을 설정합니다. npm 실행 빌드:람다,
  6. 환경 변수(MONGODB_URI)를 추가합니다,
  7. 배포...

그리고 TAAADAAAA...

pageNotFound.png

이는 엔드포인트가 기본적으로 http://page/ 하지만 http://page/.netlify/functions/graphql.

withoutRedirect.png

어떻게 해결하나요? 매우 간단합니다. 그냥 _redirect 와 함께:

/ /.netlify/functions/graphql 200!

다시 배포하고 확인합니다.

redirect.png

마음에 드셨기를 바랍니다! 자유롭게 개선하고 변경해 주세요.

자세히 읽어보세요:

잘못된 코딩 관행으로 프로젝트를 죽이지 않는 방법은 무엇인가요?

웹 앱 보안. Target="_blank" 취약점

웹 앱 보안 - XSS 취약점

관련 문서

소프트웨어 개발

미래 지향적인 웹 앱 구축: The Codest의 전문가 팀이 제공하는 인사이트

The Codest가 최첨단 기술로 확장 가능한 대화형 웹 애플리케이션을 제작하고 모든 플랫폼에서 원활한 사용자 경험을 제공하는 데 탁월한 성능을 발휘하는 방법을 알아보세요. Adobe의 전문성이 어떻게 디지털 혁신과 비즈니스를 촉진하는지 알아보세요...

최신
소프트웨어 개발

라트비아에 본사를 둔 10대 소프트웨어 개발 기업

최신 기사에서 라트비아 최고의 소프트웨어 개발 기업과 그들의 혁신적인 솔루션에 대해 알아보세요. 이러한 기술 리더들이 어떻게 귀사의 비즈니스를 향상시키는 데 도움을 줄 수 있는지 알아보세요.

thecodest
엔터프라이즈 및 스케일업 솔루션

Java 소프트웨어 개발 필수 사항: 성공적인 아웃소싱을 위한 가이드

The Codest로 효율성을 높이고 전문 지식을 활용하며 프로젝트 성공을 이끌 수 있는 성공적인 outsourcing Java 소프트웨어 개발에 대한 이 필수 가이드를 살펴보세요.

thecodest
소프트웨어 개발

폴란드 아웃소싱을 위한 최고의 가이드

폴란드에서 outsourcing가 급증한 것은 경제, 교육, 기술 발전으로 인한 IT 성장과 비즈니스 친화적인 환경이 조성된 덕분입니다.

더코데스트
엔터프라이즈 및 스케일업 솔루션

IT 감사 도구 및 기술에 대한 완벽한 가이드

IT 감사는 안전하고 효율적이며 규정을 준수하는 시스템을 보장합니다. 전체 기사를 읽고 그 중요성에 대해 자세히 알아보세요.

The Codest
야쿱 야쿠보비치 CTO & 공동 설립자

지식창고를 구독하고 IT 분야의 전문 지식을 최신 상태로 유지하세요.

    회사 소개

    The Codest - 폴란드에 기술 허브를 둔 국제 소프트웨어 개발 회사입니다.

    영국 - 본사

    • 사무실 303B, 182-184 하이 스트리트 노스 E6 2JA
      영국 런던

    폴란드 - 현지 기술 허브

    • 파브리츠나 오피스 파크, 알레야
      포코주 18, 31-564 크라쿠프
    • 뇌 대사관, 콘스트럭터스카
      11, 02-673 바르샤바, 폴란드

      The Codest

    • 홈
    • 회사 소개
    • 서비스
    • Case Studies
    • 방법 알아보기
    • 채용 정보
    • 사전

      서비스

    • IT 자문
    • 소프트웨어 개발
    • 백엔드 개발
    • 프론트엔드 개발
    • Staff Augmentation
    • 백엔드 개발자
    • 클라우드 엔지니어
    • 데이터 엔지니어
    • 기타
    • QA 엔지니어

      리소스

    • 외부 소프트웨어 개발 파트너와의 협력에 대한 사실과 오해
    • 미국에서 유럽으로: 미국 스타트업이 유럽으로 이전을 결정하는 이유
    • 테크 오프쇼어 개발 허브 비교: 테크 오프쇼어 유럽(폴란드), 아세안(필리핀), 유라시아(터키)
    • CTO와 CIO의 주요 과제는 무엇인가요?
    • The Codest
    • The Codest
    • The Codest
    • Privacy policy
    • 웹사이트 이용 약관

    저작권 © 2025 by The Codest. 모든 권리 보유.

    ko_KRKorean
    en_USEnglish de_DEGerman sv_SESwedish da_DKDanish nb_NONorwegian fiFinnish fr_FRFrench pl_PLPolish arArabic it_ITItalian jaJapanese es_ESSpanish nl_NLDutch etEstonian elGreek ko_KRKorean