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 취약점

관련 문서

상승하는 화살표와 비용 효율성 또는 절감을 상징하는 금화가 있는 하락하는 막대 차트의 추상적인 그림. 밝은 회색 바탕에 "In Code We Trust"라는 슬로건과 함께 왼쪽 상단에 The Codest 로고가 표시됩니다.
소프트웨어 개발

제품 품질 저하 없이 개발팀을 확장하는 방법

개발팀을 확장하고 계신가요? 제품 품질을 저하시키지 않고 성장하는 방법을 알아보세요. 이 가이드에서는 확장할 시기의 징후, 팀 구조, 채용, 리더십 및 도구와 더불어 The Codest가 어떻게...

최신
소프트웨어 개발

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

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

최신
소프트웨어 개발

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

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

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

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

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

thecodest
소프트웨어 개발

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

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

더코데스트

지식창고를 구독하고 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