클라이언트가 원하는 것을 정확히 요청할 수 있도록 강력한 API를 만드는 방법에 대해 궁금해 본 적이 있으신가요? 그것이 바로 GraphQL의 마법이며, Apollo Server를 사용하면 생각보다 훨씬 간단하게 구축할 수 있습니다! REST의 고정된 엔드포인트에 지쳤다면, GraphQL은 유연성을 제공하며 Apollo Server는 이를 실현하기 위한 최고의 도구입니다. 이 대화형 튜토리얼에서는 프로젝트 초기화부터 쿼리 및 뮤테이션 테스트에 이르기까지 Apollo Server를 사용하여 GraphQL 서버를 설정하는 과정을 단계별로 안내합니다. JavaScript를 사용하든 TypeScript를 사용하든, 순식간에 실행 가능한 서버를 갖게 될 것입니다. GraphQL과 Apollo Server로 멋진 것을 만들어 봅시다!
개발팀이 최고의 생산성으로 함께 작업할 수 있는 통합된 올인원 플랫폼을 원하시나요?
Apidog는 당신의 모든 요구 사항을 충족하며, 훨씬 더 저렴한 가격으로 Postman을 대체합니다!
GraphQL과 Apollo Server를 선택하는 이유?
본격적으로 시작하기 전에, GraphQL과 Apollo Server가 왜 그렇게 강력한 조합인지 이야기해 봅시다. 2012년 Facebook에서 개발되어 2015년 오픈 소스로 공개된 GraphQL은 클라이언트가 특정 데이터를 요청할 수 있도록 하는 API용 쿼리 언어로, REST API에서 흔히 발생하는 과도한 데이터 가져오기(over-fetching) 및 부족한 데이터 가져오기(under-fetching) 문제를 줄여줍니다. 여러 엔드포인트 대신, 맞춤형 응답을 제공하는 하나의 스마트한 엔드포인트를 갖게 됩니다. 이는 효율적이고 유연하며, 복잡한 데이터 요구 사항을 가진 최신 앱에 완벽합니다.
여기 Apollo Server가 있습니다. Apollo GraphQL의 오픈 소스, 커뮤니티 주도 GraphQL 서버입니다. 프로덕션 준비가 되어 있으며, Node.js를 지원하고 MongoDB 또는 PostgreSQL과 같은 데이터베이스와 원활하게 통합됩니다. 스키마 스티칭, 캐싱, 실시간 구독과 같은 기능을 통해 확장 가능한 API를 구축하기 위한 원스톱 솔루션입니다. 또한, 초보자에게 친숙하면서도 전문가에게 강력합니다. Express-GraphQL과 같은 대안과 비교할 때, Apollo Server는 더 나은 성능 모니터링과 더 쉬운 설정을 제공합니다. 블로그, 전자상거래 사이트 또는 모바일 백엔드를 구축하는 경우, 이 조합은 시간과 노력을 절약해 줄 것입니다. 기대되시나요? 프로젝트를 설정해 봅시다!
JavaScript 또는 TypeScript로 프로젝트 설정하기
기초부터 시작해 봅시다. Node.js 프로젝트를 설정하고 필요한 패키지를 설치할 것입니다. 간단함을 위해 JavaScript를 선택하거나 타입 안정성을 위해 TypeScript를 선택할 수 있습니다. 둘 다 Apollo Server와 잘 작동합니다.
1단계: 프로젝트 초기화
새 폴더 생성:
- 터미널을 열고 프로젝트 디렉토리를 생성합니다:
mkdir graphql-apollo-server
cd graphql-apollo-server
Node.js 초기화:
- 다음 명령어를 실행합니다:
npm init -y
npm pkg set type="module"- 이는 의존성 관리를 위한
package.json파일을 생성하고 ES 모듈을 사용하는 프로젝트를 설정합니다.
2단계: 의존성 설치
Apollo Server는 두 가지 주요 패키지를 필요로 합니다: 서버를 위한 @apollo/server와 핵심 GraphQL 라이브러리를 위한 graphql입니다. TypeScript의 경우, 타입과 빌드 단계를 추가할 것입니다.
의존성 설치:
npm install @apollo/server graphql
JavaScript의 경우:
간단히 package.json 파일의 기본 scripts 항목을 다음 type 및 scripts 항목으로 대체합니다:
{
// ...etc.
"type": "module",
"scripts": {
"start": "node index.js"
}
// other dependencies
}TypeScript의 경우 (권장):
1. TypeScript 초기화:
npm install --save-dev typescript @types/node- 루트 디렉토리에
tsconfig.json파일을 생성하고 다음을 포함하도록 편집합니다:
{
"compilerOptions": {
"rootDirs": ["src"],
"outDir": "dist",
"lib": ["es2023"],
"target": "es2023",
"module": "esnext",
"moduleResolution": "node",
"esModuleInterop": true,
"types": ["node"]
}
}2. 마지막으로, package.json 파일의 scripts 항목을 다음 type 및 scripts 항목으로 대체합니다:
{
// ...etc.
"type": "module",
"scripts": {
"compile": "tsc",
"start": "npm run compile && node ./dist/index.js"
}
// other dependencies
}
전문가 팁: TypeScript가 처음이라면, 스키마와 리졸버에 타입 안전성을 추가하여 오류를 조기에 잡아냅니다. JavaScript는 프로토타입에 더 빠릅니다. 프로젝트 규모에 따라 선택하세요.
3단계: 서버 파일 생성
프로젝트 루트에 src 폴더를 생성하고 새 폴더 안에 index.ts (JavaScript의 경우 index.js) 파일을 추가합니다. 이곳에서 스키마와 리졸버를 정의할 것입니다.

간단한 쿼리 테스트하기
첫 번째 쿼리인 간단한 "hello" 메시지를 만들어 봅시다. 이를 통해 GraphQL의 타입 정의(스키마)와 리졸버(데이터를 가져오는 함수)를 소개합니다.
GraphQL 스키마 정의하기
스키마는 API의 청사진입니다. @apollo/server에 포함된 graphql-tag의 gql 태그를 사용하여 정의합니다.
index.ts (또는 index.js)에서:
import { ApolloServer } from '@apollo/server';
import { startStandaloneServer } from '@apollo/server/standalone';
// Define GraphQL schema
const typeDefs = `
type Query {
hello: String
}
`;
// Define resolvers
const resolvers = {
Query: {
hello: () => "Hello! Welcome to my server",
},
};
// Create and start the server
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`🚀 Server ready at: ${url}`);
JavaScript의 경우, 타입을 임포트합니다:
const { ApolloServer } = '@apollo/server';
const { startStandaloneServer } = '@apollo/server/standalone';
// Rest is the same
서버 실행하기
서버를 시작합니다:
node index.js # For JavaScript
npm start # For TypeScript
브라우저에서 http://localhost:4000에 접속합니다. 쿼리 테스트를 위한 웹 기반 IDE인 GraphQL Playground를 볼 수 있을 것입니다.

쿼리 테스트하기
플레이그라운드의 왼쪽 패널에서 다음 쿼리를 실행합니다:
query {
hello
}
"실행"을 클릭합니다. 오른쪽에 다음이 표시됩니다:
{
"data": {
"hello": "Hello! Welcome to my server"
}
}

성공입니다! 이 간단한 쿼리는 GraphQL의 기본을 보여줍니다: hello 필드가 문자열을 반환하는 Query 타입. 리졸버는 데이터를 제공하는 "두뇌" 역할을 합니다. 이 경우, 정적 메시지를 제공합니다. 이는 설정을 확인하는 훌륭한 시작점입니다.
복합 타입으로 쿼리 테스트하기
이제 사용자 정의 타입으로 깊이를 더해 봅시다. Book 타입을 생성하고 책 목록을 가져오는 쿼리를 만들 것입니다. 이는 GraphQL이 구조화된 데이터를 처리하는 방법을 보여줍니다.
스키마 업데이트하기
typeDefs를 수정하여 Book 타입을 포함시킵니다:
const typeDefs = gql`
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
`;
샘플 데이터 추가하기
typeDefs 아래에 새로운 Book 타입에 대한 다음 샘플 데이터를 추가합니다:
// Sample data
const books = [
{
title: 'The Awakening',
author: 'Kate Chopin',
},
{
title: 'City of Glass',
author: 'Paul Auster',
},
];리졸버 업데이트하기
books 타입에 대한 리졸버 내용을 다음으로 대체합니다:
const resolvers = {
Query: {
books: () => books
}
};
서버를 재시작하고 플레이그라운드로 돌아갑니다.
쿼리 테스트하기
실행:
query GetBooks {
books {
title
author
}
}
결과:
{
"data": {
"books": [
{
"title": "The Awakening",
"author": "Kate Chopin"
},
{
"title": "City of Glass",
"author": "Paul Auster"
}
]
}
}
멋지죠? 이 쿼리는 Book 객체 배열을 가져옵니다. GraphQL은 클라이언트가 원하는 필드를 정확히 지정할 수 있도록 합니다. 더도 덜도 없이 말이죠. author를 생략하면 제목만 반환합니다. 이러한 유연성 때문에 GraphQL이 데이터가 많은 앱에서 REST보다 뛰어납니다.

데이터 추가를 위한 뮤테이션 테스트하기
쿼리는 읽기용이지만, 뮤테이션은 쓰기용입니다. 새 책을 생성하는 뮤테이션을 추가하여 GraphQL이 데이터 생성을 어떻게 처리하는지 보여줍시다.
스키마 업데이트하기
Mutation 타입을 추가합니다:
const typeDefs = `
type Book {
title: String
author: String
}
type Query {
books: [Book]
}
type Mutation {
createBook(title: String!, author: String!): Book
}
`;
!는 필수 필드를 의미합니다.
리졸버 업데이트하기
const resolvers = {
Query: {
books: () => books,
},
Mutation: {
createBook: (_: any, { title, author }: { title: string; author: string }) => {
const newBook = { title, author };
books.push(newBook);
return newBook;
}
}
};
뮤테이션 테스트하기
플레이그라운드에서 실행:
mutation CreateBook{
createBook(title: "Harry Potter", author: "J.K Rowling") {
author
title
}
}
결과:
{
"data": {
"createBook": {
"title": "Harry Potter",
"author": "J.K Rowling"
}
}
}

확인을 위해 GetBooks 쿼리를 다시 실행합니다:
query GetBooks {
books {
title
author
}
}
결과:
{
"data": {
"books": [
{
"title": "The Awakening",
"author": "Kate Chopin"
},
{
"title": "City of Glass",
"author": "Paul Auster"
},
{
"title": "Harry Potter",
"author": "J.K Rowling"
}
]
}
}

새로운 책이 추가되었습니다! 뮤테이션은 생성된 데이터를 반환하여 클라이언트가 즉각적인 피드백을 받을 수 있도록 합니다. 프로덕션 환경에서는 MongoDB와 같은 DB에 연결하여 영속성을 확보하세요.
JavaScript vs TypeScript: 무엇을 선택할까? 빠른 프로토타입의 경우 JavaScript가 좋습니다. 상용구 코드가 적기 때문입니다. 하지만 더 큰 프로젝트에서는 TypeScript가 스키마와 리졸버에 대한 타입 안전성으로 빛을 발합니다. TS는 오류를 조기에 잡아내어 GraphQL 서버를 더욱 견고하게 만듭니다.
복잡성 추가: ID 및 인수를 사용한 쿼리
실제처럼 만들기 위해 책에 ID를 추가하고 제목으로 가져오는 쿼리를 추가합니다.
스키마 업데이트:
const typeDefs = `
type Book {
id: ID!
title: String
author: String
}
type Query {
books: [Book]
book(title: String!): Book
}
type Mutation {
createBook(title: String!, author: String!): Book
}
`;데이터 및 리졸버 업데이트:
// Sample data
const books = [
{
id: 1,
title: 'The Awakening',
author: 'Kate Chopin',
},
{
id: 2,
title: 'City of Glass',
author: 'Paul Auster',
},
];
// Resolvers
const resolvers = {
Query: {
books: () => books,
book: (_: any, { title }: { title: string }) => books.find(book => book.title === title),
},
Mutation: {
createBook: (_: any, { title, author }: { title: string; author: string }) => {
const newBook = { id: books.length + 1, title, author };
books.push(newBook);
return newBook;
}
}
};
쿼리 테스트:
query GetBook {
book(title: "The Awakening") {
id
title
author
}
}
결과:
{
"data": {
"book": {
"id": "1",
"title": "The Awakening",
"author": "Kate Chopin"
}
}
}
이는 쿼리의 인수를 보여주며, 클라이언트가 데이터를 효율적으로 필터링할 수 있도록 합니다.

Apollo Server와 GraphQL을 위한 모범 사례
- 스키마 설계: 여러 파일로 모듈화하여 유지합니다.
- 오류 처리: 사용자 정의 오류에
ApolloError를 사용합니다. - 성능:
@apollo/cache-control로 캐싱을 활성화합니다. - 구독:
apollo-server-express로 실시간 기능을 추가합니다. - 모니터링: 지표를 위해 Apollo Studio를 사용합니다.
일반적인 문제 해결
- 서버가 시작되지 않나요? Node 버전(14+)과 의존성을 확인하세요.
- 쿼리 오류? 스키마/리졸버 일치 여부를 확인하세요.
- CORS 문제? 서버 옵션에
{ cors: { origin: '*' } }를 추가하세요. - TS 오류?
@types/graphql및@types/node를 설치하세요.
결론
우리는 헬로 쿼리부터 뮤테이션까지 Apollo Server로 견고한 GraphQL 서버를 구축했습니다. JS든 TS든, 유연한 API를 생성할 준비가 되었습니다. 실험하고, 구독을 추가하고, Heroku에 배포해 보세요. GraphQL과 Apollo Server는 효율적인 API를 향한 당신의 티켓입니다!
Apidog는 GraphQL 테스트도 지원하므로, 완전히 무료로 확인해 보세요!

