PageSourceSearch

https://supawer0728.github.io/2020/03/09/graphql-java2/

html supawer0728.github.io collected 2026-10-03 10:00:23 UTC 77,577 bytes, 1,499 lines download raw bytes

1<!DOCTYPE html>
2
3
4
5  
6
7
8<html class="theme-next gemini use-motion" lang="ko">
9<head>
10  <meta charset="UTF-8"/>
11<meta http-equiv="X-UA-Compatible" content="IE=edge" />
12<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
13<meta name="theme-color" content="#222">
14
15
16
17
18
19
20
21
22
23<meta http-equiv="Cache-Control" content="no-transform" />
24<meta http-equiv="Cache-Control" content="no-siteapp" />
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41  
42  
43  <link href="/lib/fancybox/source/jquery.fancybox.css?v=2.1.5" rel="stylesheet" type="text/css" />
44
45
46
47
48  
49  
50  
51  
52
53  
54    
55    
56  
57
58  
59
60  
61
62  
63    
64      
65    
66
67    
68  
69
70  
71
72  
73    
74    
75    <link href="//fonts.googleapis.com/css?family=Nanum Gothic:300,300italic,400,400italic,700,700italic|Nanum Pen Script:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
76  
77
78
79
80
81
82
83<link href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2" rel="stylesheet" type="text/css" />
84
85<link href="/css/main.css?v=5.1.4" rel="stylesheet" type="text/css" />
86
87
88  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-icon-180x180.png?v=5.1.4">
89
90
91  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32.png?v=5.1.4">
92
93
94  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16.png?v=5.1.4">
95
96
97
98  <link rel="manifest" href="/images/manifest.json">
99
100
101  <meta name="msapplication-config" content="/images/browserconfig.xml" />
102
103
104
105  <meta name="keywords" content="graphql," />
106
107
108
109
110
111  <link rel="alternate" href="/feed.xml" title="기록은 재산이다" type="application/atom+xml" />
112
113
114
115
116
117
118<meta name="description" content="머리말지난 포스트에서는 GraphQL에 대해서 간단하게 테스트를 해보았다.이번 포스트에서는 GraphQL의 Learn에 나오는 내용을 따라 설명을 덧붙이려고 한다.설명과 더불어 GraphiQL을 붙여 즉각 실행해볼 수 있게 구성하였다.  테스트는 Heroku를 사용하고 있다.Heroku를 무료로 사용할 시 유휴시간이 30분이 넘는 경우, 애플리케이션이 sl">
119<meta property="og:type" content="article">
120<meta property="og:title" content="GraphQL Vol.2 - GraphQL 기초">
121<meta property="og:url" content="https://supawer0728.github.io/2020/03/09/graphql-java2/index.html">
122<meta property="og:site_name" content="기록은 재산이다">
123<meta property="og:description" content="머리말지난 포스트에서는 GraphQL에 대해서 간단하게 테스트를 해보았다.이번 포스트에서는 GraphQL의 Learn에 나오는 내용을 따라 설명을 덧붙이려고 한다.설명과 더불어 GraphiQL을 붙여 즉각 실행해볼 수 있게 구성하였다.  테스트는 Heroku를 사용하고 있다.Heroku를 무료로 사용할 시 유휴시간이 30분이 넘는 경우, 애플리케이션이 sl">
124<meta property="og:locale" content="ko_KR">
125<meta property="article:published_time" content="2020-03-08T16:55:00.000Z">
126<meta property="article:modified_time" content="2020-03-08T17:02:43.141Z">
127<meta property="article:author" content="supawer0728">
128<meta property="article:tag" content="graphql">
129<meta name="twitter:card" content="summary">
130
131
132
133<script type="text/javascript" id="hexo.configurations">
134  var NexT = window.NexT || {};
135  var CONFIG = {
136    root: '/',
137    scheme: 'Gemini',
138    version: '5.1.4',
139    sidebar: {"position":"left","display":"hide","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
140    fancybox: true,
141    tabs: true,
142    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
143    duoshuo: {
144      userId: '0',
145      author: '작성자'
146    },
147    algolia: {
148      applicationID: '',
149      apiKey: '',
150      indexName: '',
151      hits: {"per_page":10},
152      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
153    }
154  };
155</script>
155
156
157
158
159  <link rel="canonical" href="https://supawer0728.github.io/2020/03/09/graphql-java2/"/>
160
161
162
163
164
165  <title>GraphQL Vol.2 - GraphQL 기초 | 기록은 재산이다</title>
166  
167
168
169
170
171<!-- Global site tag (gtag.js) - Google Analytics -->
vendor: 64 bytes, lines 171-172
171
172<script async src="https://www.googletagmanager.com/gtag/js?id=
172UA-115481794-1
vendor: 12 bytes, line 172
172"></script>
173<script>
174  
vendor: 124 bytes, line 174
174window.dataLayer = window.dataLayer || [];function gtag(){dataLayer.push(arguments);}gtag('js', new Date());gtag('config', '
174UA-115481794-1
vendor: 4 bytes, line 174
174');
175</script>
175
176
177
178
179
180
181  
182<!-- Global site tag (gtag.js) - Google Analytics -->
183<script data-ad-client="" async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
183
184
185
186<meta name="generator" content="Hexo 4.2.0"></head>
187
188<body itemscope itemtype="http://schema.org/WebPage" lang="ko">
189
190  
191  
192    
193  
194
195  <div class="container sidebar-position-left page-post-detail">
196    <div class="headband"></div>
197
198    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
199      <div class="header-inner"><div class="site-brand-wrapper">
200  <div class="site-meta ">
201    
202
203    <div class="custom-logo-site-title">
204      <a href="/"  class="brand" rel="start">
205        <span class="logo-line-before"><i></i></span>
206        <span class="site-title">기록은 재산이다</span>
207        <span class="logo-line-after"><i></i></span>
208      </a>
209    </div>
210      
211        <h1 class="site-subtitle" itemprop="description">To Improve Human Life</h1>
212      
213  </div>
214
215  <div class="site-nav-toggle">
216    <button>
217      <span class="btn-bar"></span>
218      <span class="btn-bar"></span>
219      <span class="btn-bar"></span>
220    </button>
221  </div>
222</div>
223
224<nav class="site-nav">
225  
226
227  
228    <ul id="menu" class="menu">
229      
230        
231        <li class="menu-item menu-item-home">
232          <a href="/" rel="section">
233            
234              <i class="menu-item-icon fa fa-fw fa-home"></i> <br />
235            
236            홈
237          </a>
238        </li>
239      
240        
241        <li class="menu-item menu-item-tags">
242          <a href="/tags/" rel="section">
243            
244              <i class="menu-item-icon fa fa-fw fa-tags"></i> <br />
245            
246            태그
247          </a>
248        </li>
249      
250        
251        <li class="menu-item menu-item-categories">
252          <a href="/categories/" rel="section">
253            
254              <i class="menu-item-icon fa fa-fw fa-th"></i> <br />
255            
256            카테고리
257          </a>
258        </li>
259      
260        
261        <li class="menu-item menu-item-archives">
262          <a href="/archives/" rel="section">
263            
264              <i class="menu-item-icon fa fa-fw fa-archive"></i> <br />
265            
266            아카이브
267          </a>
268        </li>
269      
270
271      
272        <li class="menu-item menu-item-search">
273          
274            <a href="javascript:;" class="popup-trigger">
275          
276            
277              <i class="menu-item-icon fa fa-search fa-fw"></i> <br />
278            
279            검색
280          </a>
281        </li>
282      
283    </ul>
284  
285
286  
287    <div class="site-search">
288      
289  <div class="popup search-popup local-search-popup">
290  <div class="local-search-header clearfix">
291    <span class="search-icon">
292      <i class="fa fa-search"></i>
293    </span>
294    <span class="popup-btn-close">
295      <i class="fa fa-times-circle"></i>
296    </span>
297    <div class="local-search-input-wrapper">
298      <input autocomplete="off"
299             placeholder="Searching..." spellcheck="false"
300             type="text" id="local-search-input">
301    </div>
302  </div>
303  <div id="local-search-result"></div>
304</div>
305
306
307
308    </div>
309  
310</nav>
311
312
313
314 </div>
315    </header>
316
317    <main id="main" class="main">
318      <div class="main-inner">
319        <div class="content-wrap">
320          <div id="content" class="content">
321            
322
323  <div id="posts" class="posts-expand">
324    
325
326  
327
328  
329  
330  
331
332  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
333  
334  
335  
336  <div class="post-block">
337    <link itemprop="mainEntityOfPage" href="https://supawer0728.github.io/2020/03/09/graphql-java2/">
338
339    <meta itemprop="image" content="https://supawer0728.github.io/images/avatar.jpg">
340    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
341      <meta itemprop="name" content="supawer0728">
342      <meta itemprop="description" content="">
343      <meta itemprop="image" content="https://supawer0728.github.io/images/avatar.jpg">
344    </span>
345
346    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
347      <meta itemprop="name" content="기록은 재산이다">
348      <meta itemprop="logo" content="https://supawer0728.github.io/images/avatar.jpg">
349    </span>
350
351    
352      <header class="post-header">
353
354        
355        
356          <h2 class="post-title" itemprop="name headline">GraphQL Vol.2 - GraphQL 기초</h2>
357        
358
359        <div class="post-meta">
360          <span class="post-time">
361            
362              <span class="post-meta-item-icon">
363                <i class="fa fa-calendar-o"></i>
364              </span>
365              
366                <span class="post-meta-item-text">작성일&#58;</span>
367              
368              <time title="Post created" itemprop="dateCreated datePublished" datetime="2020-03-09T01:55:00+09:00">
369                2020-03-09
370              </time>
371            
372
373            
374              <span class="post-meta-divider">|</span>
375            
376
377            
378              <span class="post-meta-item-icon">
379                <i class="fa fa-calendar-check-o"></i>
380              </span>
381              
382                <span class="post-meta-item-text">수정일&#58;</span>
383              
384              
385              <time title="수정일" itemprop="dateModified" datetime="2020-03-08T00:00:00+09:00">
386                2020-03-08
387              </time>
388              
389            
390          </span>
391
392          
393            <span class="post-category" >
394            
395              <span class="post-meta-divider">|</span>
396            
397              <span class="post-meta-item-icon">
398                <i class="fa fa-folder-o"></i>
399              </span>
400              
401                <span class="post-meta-item-text">In</span>
402              
403              
404                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
405                  <a href="/categories/graphql/" itemprop="url" rel="index">
406                    <span itemprop="name">graphql</span>
407                  </a>
408                </span>
409
410                
411                
412              
413            </span>
414          
415
416          
417            
418              <span class="post-comments-count">
419                <span class="post-meta-divider">|</span>
420                <span class="post-meta-item-icon">
421                  <i class="fa fa-comment-o"></i>
422                </span>
423                <a href="/2020/03/09/graphql-java2/#comments" itemprop="discussionUrl">
424                  <span class="post-comments-count disqus-comment-count"
425                        data-disqus-identifier="2020/03/09/graphql-java2/" itemprop="commentCount"></span>
426                </a>
427              </span>
428            
429          
430
431          
432          
433
434          
435
436          
437
438          
439
440        </div>
441      </header>
442    
443
444    
445    
446    
447    <div class="post-body" itemprop="articleBody">
448
449      
450      
451
452      
453        <h1 id="머리말"><a href="#머리말" class="headerlink" title="머리말"></a>머리말</h1><p>지난 포스트에서는 GraphQL에 대해서 간단하게 테스트를 해보았다.<br>이번 포스트에서는 <a href="https://graphql.org/learn/" target="_blank" rel="noopener">GraphQL의 Learn</a>에 나오는 내용을 따라 설명을 덧붙이려고 한다.<br>설명과 더불어 GraphiQL을 붙여 즉각 실행해볼 수 있게 구성하였다.</p>
454<blockquote>
455<p>테스트는 Heroku를 사용하고 있다.<br>Heroku를 무료로 사용할 시 유휴시간이 30분이 넘는 경우, 애플리케이션이 sleep 상태로 바뀐다.<br>그렇기 때문에 아래의 테스트 로딩이 느릴 수 있으나, 1분 이내에 로딩될 것이다.</p>
456</blockquote>
457<a id="more"></a>
458
459<h1 id="질의문-Query-변경문-Mutation"><a href="#질의문-Query-변경문-Mutation" class="headerlink" title="질의문(Query) / 변경문(Mutation)"></a>질의문(Query) / 변경문(Mutation)</h1><h3 id="필드"><a href="#필드" class="headerlink" title="필드"></a>필드</h3><p>이전 포스트에서 체험했다시피, 클라이언트에서는 스키마에서 필요로하는 필드를 서버에 요청하여 응답받을 수 있다.</p>
460<blockquote>
461<p>► 버튼을 눌러 테스트할 수 있다.</p>
462</blockquote>
463<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20user%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
464
465<p>요청의 내용과 응답의 JSON의 각 필드는 대칭된다.</p>
466<blockquote>
467<p>위 요청 내용을 수정해서 테스트할 수도 있다. title을 지우고 ► 버튼을 눌러보자.</p>
468</blockquote>
469<p>또한 객체에 포함된 하위 타입에 대한 질의를 추가할 수 있다.<br>User가 재직하고 있는 Company를 조회해보자.</p>
470<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20user%20%7B%0A%20%20%20%20name%0A%20%20%20%20company%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%20%20catchPhrase%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
471
472<p>응답에 포함된 User 또한 요청에 대응하는 JSON 구조를 취하고 있다.</p>
473<h3 id="질의-인수-Arguments"><a href="#질의-인수-Arguments" class="headerlink" title="질의 인수(Arguments)"></a>질의 인수(Arguments)</h3><p>GraphQL은 조회를 위해 인수를 줄 수 있다.</p>
474<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20user(id%3A%202)%20%7B%0A%20%20%20%20name%0A%20%20%20%20phone%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
475
476<p>REST에서는 URL Path, Query String 등으로 인수를 넘겼다.<br>GraphQL에서는 모든 필드와 중첩된 객체에 스키마로 정의한 인수를 넘길 수 있다.<br>심지어 인수는 스칼라 타입(scala: String이나 Int)에도 보낼 수 있다.<br>다음 예제를 살펴보자.</p>
477<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20user(id%3A%202)%20%7B%0A%20%20%20%20name(toUpperCase%3A%20true)%0A%20%20%20%20phone%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
478
479<h3 id="별칭-할당-Aliases"><a href="#별칭-할당-Aliases" class="headerlink" title="별칭 할당(Aliases)"></a>별칭 할당(Aliases)</h3><p>아래와 같이 질의마다 변수를 할당하듯 별칭을 줄 수 있다.<br>
479user를 조회하지만, 각각 다른 별칭으로 참조할 수 있다.</p>
480<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20me%3A%20user(id%3A%201)%20%7B%0A%20%20%20%20name%0A%20%20%20%20phone%0A%20%20%7D%0A%20%20you%3A%20user(id%3A2)%20%7B%0A%20%20%20%20name%0A%20%20%20%20phone%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
481
482
483<h3 id="Fragments"><a href="#Fragments" class="headerlink" title="Fragments"></a>Fragments</h3><p>위에서 별칭을 할당할 때, <code>me</code>와 <code>you</code>가 동일한 필드를 조회하고 있었다.<br>GraphQL은 이러한 반복을 없앨 수 있도록, Fragments라 부르는 재사용 구문을 제공한다.</p>
484<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20me%3A%20user(id%3A%201)%20%7B%0A%20%20%20%20...basigInfo%0A%20%20%7D%0A%20%20you%3A%20user(id%3A2)%20%7B%0A%20%20%20%20...basigInfo%0A%20%20%7D%0A%7D%0A%0Afragment%20basigInfo%20on%20User%20%7B%0A%20%20name%0A%20%20phone%0A%20%20email%0A%20%20website%0A%7D&variables=%7B%7D"></iframe>
485
486<p><code>fragment</code>를 선언 후 fragment의 이름을 정의하고 <code>on</code> 뒤에 타입을 명시한다.<br>명시한 타입을 기반으로 fragment 내부 블럭에 넣을 수 있는 필드가 결정된다.</p>
487<h3 id="연산자명"><a href="#연산자명" class="headerlink" title="연산자명"></a>연산자명</h3><p>여태까지 사용한 질의는 사실 모두 축약형이었다.<br>질의의 원형은 <code>query 연산자명 { ... }</code>의 형식이다.</p>
488<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20meAndYou%20%7B%0A%20%20me%3A%20user(id%3A%201)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%20%20you%3A%20user(id%3A2)%20%7B%0A%20%20%20%20name%0A%20%20%7D%0A%7D&operationName=meAndYou&variables=%7B%7D"></iframe>
489
490<p>연산 타입은 <code>query(질의)</code>, <code>mutation(변경)</code>, <code>subscription(구독)</code>의 3가지로 나뉜다.<br>연산자명은 모든 연산 타입에 대해서 명명할 수 있다.<br>연산자명은 디버깅을 하거나 서버 사이드에서 로깅을 할 때 유용하게 쓰일 수 있다.</p>
491<h3 id="변수-Variables"><a href="#변수-Variables" class="headerlink" title="변수(Variables)"></a>변수(Variables)</h3><p>앞에서 인수를 통해서 다른 id의 User를 가져올 수 있는 것을 확인했었다.<br>만약 클라이언트에서 동적으로 다른 질의를 할 때, 매번 다른 인수를 가진 질의 문자열을 생성한다면 큰 낭비일 것이다.<br>질의를 템플릿으로 고정해두고, 우리는 변수를 넘길 수 있다.<br>변수는 주로 JSON 형식으로 정의한다.</p>
492<blockquote>
493<p>하단의 <code>QUERY VARIABLES</code>를 참조.</p>
494</blockquote>
495<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20findAllPostsWithLimit(%24limit%3A%20Int)%20%7B%0A%20%20posts(limit%3A%20%24limit)%20%7B%0A%20%20%20%20title%0A%20%20%09comments(limit%3A%20%24limit)%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&operationName=findAllPostsWithLimit&variables=%7B%0A%20%20%22limit%22%3A%202%0A%7D"></iframe>
496
497<p>Http Request의 모양은 다음과 같다.</p>
498<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">POST &#x2F;graphql</span><br><span class="line">Host: localhost</span><br><span class="line">&#123;</span><br><span class="line">  &quot;query&quot;: &quot;query findAllPostsWithLimit($limit: Int!) &#123; ... &#125;&quot;,</span><br><span class="line">  &quot;variables&quot;: &#123;</span><br><span class="line">    &quot;limit&quot;: 2</span><br><span class="line">  &#125;,</span><br><span class="line">  &quot;operationName&quot;: &quot;findAllPostsWithLimit&quot;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
499
500<p>이제 클라이언트에서는 질의를 매번 수정할 필요 없이, 변수만 정의해서 동적으로 다른 질의를 할 수 있다.<br>fragment에서도 변수를 그대로 사용할 수 있다.</p>
501<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">fragment limitedComments on Post &#123;</span><br><span class="line">  title</span><br><span class="line">  body</span><br><span class="line">  comments(limit: $limit) &#123;</span><br><span class="line">    title</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
502
503<h3 id="변수-정의"><a href="#변수-정의" class="headerlink" title="변수 정의"></a>변수 정의</h3><p>위와 같이 <code>$limit: Int</code>로 변수를 정의할 수 있다.<br>변수는 scala, enum, 혹은 input 타입으로 정의할 수 있다.<br>
503변수로 복잡한 객체를 넘겨야 하는 경우 서버에서 제공하는 input 타입을 사용한다.</p>
504<p>위의 질의에서는 <code>Int</code> 타입으로 선언되었기 때문에 변수가 없어도 정상적으로 동작한다.<br>필수값으로 지정하려면 <code>Int!</code>로 정의해야 한다.<br>변수 없이 정상 동작하는지 확인해보자.</p>
505<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20findAllPostsWithLimit(%24limit%3A%20Int)%20%7B%0A%20%20posts(limit%3A%20%24limit)%20%7B%0A%20%20%20%20title%0A%20%20%7D%0A%7D&operationName=findAllPostsWithLimit&variables=%7B%7D"></iframe>
506
507<h3 id="변수-기본값"><a href="#변수-기본값" class="headerlink" title="변수 기본값"></a>변수 기본값</h3><p>변수를 정의할 때 기본값을 넣을 수 있다.</p>
508<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20findAllPostsWithLimit(%24limit%3A%20Int%20%3D%202)%20%7B%0A%20%20posts(limit%3A%20%24limit)%20%7B%0A%20%20%20%20title%0A%20%20%7D%0A%7D&operationName=findAllPostsWithLimit&variables=%7B%7D"></iframe>
509
510<h3 id="지시자-Directives"><a href="#지시자-Directives" class="headerlink" title="지시자(Directives)"></a>지시자(Directives)</h3><p>변수가 인수를 동적으로 전달할 수 있었다면, 지시자는 질의를 동적으로 변경할 수 있다.</p>
511<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20post(%24id%3A%20Int!%2C%20%24withComments%3A%20Boolean!)%20%7B%0A%20%20post(id%3A%20%24id)%20%7B%0A%20%20%20%20title%0A%20%20%20%20comments%20%40include(if%3A%20%24withComments)%20%7B%0A%20%20%20%20%20%20body%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&operationName=post&variables=%7B%0A%20%20%22id%22%3A%201%2C%0A%20%20%22withComments%22%3A%20false%0A%7D"></iframe>
512
513<p>기본적으로 GraphQL은 아래 2개의 지시자를 서버에서 구현하도록 정의했다.</p>
514<ul>
515<li><code>@include(if: Boolean)</code>: 인수가 <code>true</code>일 때에만 해당 필드를 포함.</li>
516<li><code>@skip(if: Boolean)</code>: 인수가 <code>true</code>이면 해당 필드를 건너뜀.</li>
517</ul>
518<p>서버는 새로운 지시자를 정의하여 구현할 수도 있다.</p>
519<h3 id="변경문-Mutations"><a href="#변경문-Mutations" class="headerlink" title="변경문(Mutations)"></a>변경문(Mutations)</h3><p>REST API에서 컨벤션 상에서 GET 요청으로는 변경을 일으키지 않듯이, GraphQL에서도 mutation으로 데이터를 변경한다.</p>
520<p>아래 mutation을 살펴보자</p>
521<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">mutation CreateReviewForEpisode($ep: Episode!, $review: ReviewInput!) &#123;</span><br><span class="line">  createReview(episode: $ep, review: $review) &#123;</span><br><span class="line">    stars</span><br><span class="line">    commentary</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"># variables</span><br><span class="line">&#123;</span><br><span class="line">  &quot;ep&quot;: &quot;JEDI&quot;,</span><br><span class="line">  &quot;review&quot;: &#123;</span><br><span class="line">    &quot;stars&quot;: 5,</span><br><span class="line">    &quot;commentary&quot;: &quot;This is a great movie!&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
522
523<p>위와 같은 mutation에서 createReview를 통해 <code>ep</code>, <code>review</code> 변수가 제공되고,<br><code>star</code>와 <code>commentary</code>를 필드로 갖는 오브젝트가 반환된다.<br>즉 Java 서버의 경우 아래와 같은 메서드 시그니처로 정의할 수 있다.</p>
524<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">ReviewService</span> <span class="keyword">implements</span> <span class="title">GraphQLQueryResolver</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> Review <span class="title">createReview</span><span class="params">(Integer stars, ReviewInput review)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
525
526<p>위의 변수로 주어진 <code>review</code>가 앞에서 이야기했던 input 타입이다.</p>
527<h3 id="mutation의-필드들"><a href="#mutation의-필드들" class="headerlink" title="mutation의 필드들"></a>mutation의 필드들</h3><p>질의문과 변경문의 가장 큰 차이점은, 질의문은 각 필드에 대해서 병렬로 실행되는 반면에 변경문의 각 필드는 순차 실행된다는 것이다.<br>즉, 한 번의 요청으로 <code>count</code>를 1 증가시키는 변경문을 두 개를 보낼 때,<br>첫 번째 요청이 완료되고 두 번째 요청이 실행되기 때문에 레이스 컨디션(Race Condition)에 빠질 우려가 없다.</p>
528<h3 id="Inline-Fragments"><a href="#Inline-Fragments" class="headerlink" title="Inline Fragments"></a>Inline Fragments</h3><p>GraphQL은 타입을 추상화할 수 있는 interface를 제공하고 있으며, 다중 상속과 비슷한 union 타입 또한 제공하고 있다.</p>
529<p>탈것에 대해 다음과 같이 스키마가 정의되어 있다.</p>
530<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">interface Vehicle &#123;</span><br><span class="line">    id: Int!</span><br><span class="line">    manufacturer: String!</span><br><span class="line">    name: String!</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">type Car implements Vehicle &#123;</span><br><span class="line">    id: Int!</span><br><span class="line">    manufacturer: String!</span><br><span class="line">    name: String!</span><br><span class="line">
530    tire: String!</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">type Airplane implements Vehicle &#123;</span><br><span class="line">    id: Int!</span><br><span class="line">    manufacturer: String!</span><br><span class="line">    name: String!</span><br><span class="line">    flyLimit: String!</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
531
532<p>이러한 탈것 추상 타입의 구현체인 자동차, 비행기에 따라 다르게 동작하도록 하려면 아래와 같이 inline fragment를 추가한다.</p>
533<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20vehicle(id%3A%201)%20%7B%0A%20%20%20%20name%0A%20%20%20%20manufacturer%0A%20%20%20%20...%20on%20Car%20%7B%0A%20%20%20%20%20%20tire%0A%20%20%20%20%7D%0A%20%20%20%20...%20on%20Airplane%20%7B%0A%20%20%20%20%20%20flyLimit%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D
534&variables=%7B%7D"></iframe>
535
536<h3 id="메타-필드"><a href="#메타-필드" class="headerlink" title="메타 필드"></a>메타 필드</h3><p>위에서의 추상 타입에 대한 구현체에 따라 클라이언트의 동작이 달라져야 하는 경우가 있을 수 있다.<br>이럴 때에는 <code>__typename</code> 메타 필드를 추가하여 실제 어떤 구현체가 응답으로 오는지 확인할 수 있다.</p>
537<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20vehicle(id%3A%201)%20%7B%0A%20%20%20%20__typename%0A%20%20%20%20name%0A%20%20%20%20manufacturer%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
538
539<h1 id="스키마와-타입"><a href="#스키마와-타입" class="headerlink" title="스키마와 타입"></a>스키마와 타입</h1><h3 id="타입"><a href="#타입" class="headerlink" title="타입"></a>타입</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  vehicle &#123;</span><br><span class="line">    name</span><br><span class="line">    manufacturer</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
540
541<p>위 예제와 같이 GraphQL에서 타입은:</p>
542<ul>
543<li>root 오브젝트로 시작한다.</li>
544<li>root 오브젝트에 field(<code>vehicle</code>)를 선언한다.</li>
545<li>vehicle에서 받을 응답값을 선택적으로 기재한다.</li>
546</ul>
547<p>요청 스키마를 따라 구조화된 JSON 응답이 오기 때문에, 서버 응답을 예측할 수 있다.<br>때문에 클라이언트는 어떤 구조로 요청을 보낼 수 있는가에 대해 알아야 하는데 이를 스키마로 정의해두었다.</p>
548<p>GraphQL을 제공하는 애플리케이션은 우리가 어떤 데이터를 질의할 수 있는지를 정의한 타입 셋을 가지고 있다.<br>그리고 애플리케이션은 질의가 들어오면 이 스키마를 가지고 질의 된 값들의 유효성을 검증한다.</p>
549<h3 id="객체-타입과-필드"><a href="#객체-타입과-필드" class="headerlink" title="객체 타입과 필드"></a>객체 타입과 필드</h3><p>GraphQL의 스키마에서 가장 기초적인 것은 오브젝트 타입이다.</p>
550<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">type Album &#123;</span><br><span class="line">  name: String!</span><br><span class="line">  photos: [Photo!]!</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
551
552<ul>
553<li>타입의 이름을 <code>Album</code>로 지었다.</li>
554<li><code>name</code>과 <code>photo</code>는 <code>Album</code> 객체의 필드이다.</li>
555<li><code>String</code>은 GraphQL에서 기본으로 제공하는 스칼라 타입이다. 스칼라 타입은 다시 알아보기로 하자.</li>
556<li><code>String!</code>에서 <code>!</code>는 해당 필드가 Null을 허용하지 않음을 뜻한다.(non-null)</li>
557<li><code>[Photo!]!</code>는 <code>Photo</code> 객체의 배열을 뜻한다.<ul>
558<li><code>[]!</code>는 즉 <code>photos</code> 변수는 Null일 수 없음을 의미한다.</li>
559<li><code>Photo!</code>는 배열의 각 요소가 Null일 수 없음을 의미한다.</li>
560</ul>
561</li>
562</ul>
563<h3 id="Query와-Mutation-타입"><a href="#Query와-Mutation-타입" class="headerlink" title="Query와 Mutation 타입"></a>Query와 Mutation 타입</h3><p>GraphQL에서 대부분은 오브젝트 타입이지만, 아래의 두 타입은 특별한 취급을 받는다.</p>
564<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">schema &#123;</span><br><span class="line">  query: Query</span><br><span class="line">  mutation: Mutation</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
565
566<p>모든 GraphQL 서비스는 <code>query</code> 타입을 가지며 <code>mutation</code> 타입은 있을 수도 있고 없을 수도 있다.<br>두 타입은 일반적인 오브젝트 타입과 같지만, 모든 GraphQL의 진입점이 된다는 점에서 특별 취급을 받는다.</p>
567<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20%7B%0A%20%20user%20%7B%0A%20%20%20%20name%0A%20%20%20%20email%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
568
569<p>위 질의문을 수행하기 위해서 서비스는 아래의 스키마를 가져야 한다.</p>
570<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">type Query &#123;</span><br><span class="line">
570  user: User</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
571
572<p><code>mutation</code> 또한 비슷한 방식으로 동작한다.</p>
573<h3 id="스칼라-타입"><a href="#스칼라-타입" class="headerlink" title="스칼라 타입"></a>스칼라 타입</h3><p>GraphQL은 다음의 스칼라 타입을 기본으로 제공한다.</p>
574<ul>
575<li>Int: signed 32-bit integer.</li>
576<li>Float: signed double-precision floating-point.(64-bit)</li>
577<li>String: UTF-8 Character Sequence.</li>
578<li>Boolean</li>
579<li>ID: 고유 식별자를 나타내는 타입이다. 주로 객체를 다시 페치해오거나, 캐시키로 사용된다.<br>ID는 String과 같은 방식으로 직렬화되지만, ID는 난독화된다는 것에 의미를 가진다.</li>
580</ul>
581<p>대부분의 GraphQL 서비스는 자체적으로 스칼라 타입을 정의할 수 있다. Java도 가능하다.</p>
582<p>아래와 같이 <code>Date</code> 타입을 정의할 수 있다.</p>
583<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">scalas Date</span><br></pre></td></tr></table></figure>
584
585<p>이에 대해 어떻게 역/직렬화할 것인지는 구현하기에 달렸다.</p>
586<h3 id="Enumumeration"><a href="#Enumumeration" class="headerlink" title="Enumumeration"></a>Enumumeration</h3><p>아래와 같이 enum을 정의할 수 있다.</p>
587<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">enum UserProvider &#123;</span><br><span class="line">  NAVER</span><br><span class="line">  LINE</span><br><span class="line">  KAKAO</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
588
589<p>GraphQL 서비스는 여러 언어로 구현될 수 있으므로 각자 언어에 맞게 enum을 구현한다.<br>enum을 객체로 다루는 언어에서는 그 혜택을 누릴 수 있겠지만,<br>Javascript와 같이 enum을 지원하지 않는 언어에서는 단순히 integer로 다뤄질 수 있다.<br><strong>하지만 이러한 서비스의 세부 동작은 클라이언트에 노출되지 않으며,</strong><br><strong>클라이언트는 정의된 enum 값들 내에서 질의, 변경을 수행할 수 있다.</strong></p>
590<h3 id="List와-Non-null"><a href="#List와-Non-null" class="headerlink" title="List와 Non-null"></a>List와 Non-null</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">type Album &#123;</span><br><span class="line">  name: String!</span><br><span class="line">  photos: [Photo!]!</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
591
592<p><code>name</code>은 <code>String</code> 타입이며 <code>!</code>를 뒤에 붙여 <code>Non-Null</code>을 명시하였다.<br>이 제약으로 인해 서버는 <code>name</code>에 <code>null</code> 값을 줄 수 없으며, <code>null</code>이 오는 경우에는 오류를 발생시킨다.<br><code>Non-Null</code>은 인수에도 선언할 수 있다.<br>아래와 같이 <code>Non-Null</code>로 선언된 인수에 <code>null</code>이 들어가면 오류가 질의를 실행할 때 오류가 발생한다.</p>
593<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=query%20getVehicleById(%24id%3A%20Int!)%7B%0A%20%20vehicle(id%3A%20%24id)%20%7B%0A%20%20%20%20name%0A%20%20%20%20manufacturer%0A%20%20%7D%0A%7D&variables=%7B%0A%20%20%22id%22%3A%20null%0A%7D&operationName=getVehicleById"></iframe>
594
595<p><code>Non-Null</code>과 <code>List</code>는 조합해서 사용할 수 있다.</p>
596<table>
597<thead>
598<tr>
599<th>myField: [String!]</th>
600<th></th>
601</tr>
602</thead>
603<tbody><tr>
604<td><code>myField: null</code></td>
605<td>valid</td>
606</tr>
607<tr>
608<td><code>myField: []</code></td>
609<td>valid</td>
610</tr>
611<tr>
612<td><code>myField: [&#39;a&#39;, &#39;b&#39;]</code></td>
613<td>valid</td>
614</tr>
615<tr>
616<td><code>myField: [&#39;a&#39;, null, &#39;b&#39;]</code></td>
617<td>error</td>
618</tr>
619</tbody></table>
620<table>
621<thead>
622<tr>
623<th>myField: [String]!</th>
624<th></th>
625</tr>
626</thead>
627<tbody><tr>
628<td><code>myField: null</code></td>
629<td>error</td>
630</tr>
631<tr>
632<td><code>myField: []</code></td>
633<td>valid</td>
634</tr>
635<tr>
636<td><code>myField: [&#39;a&#39;, &#39;b&#39;]</code></td>
637<td>valid</td>
638</tr>
639<tr>
640<td><code>myField: [&#39;a&#39;, null, &#39;b&#39;]</code></td>
641<td>valid</td>
642</tr>
643</tbody></table>
644<h3 id="인터페이스"><a href="#인터페이스" class="headerlink" title="인터페이스"></a>인터페이스</h3><p>GraphQL은 추상 타입으로 인터페이스를 제공한다.<br>인터페이스를 정의하고 구현하는 방법은 다음과 같다.</p>
645<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">interface Vehicle &#123;</span><br><span class="line">  id: Int!</span><br><span class="line">  manufacturer: String!</span><br><span class="line">  name: String!</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">type Car implements Vehicle &#123;</span><br><span class="line">  id: Int!</span><br><span class="line">  manufacturer: String!</span><br><span class="line">  name: String!</span><br><span class="line">  tire: String!</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">type Airplane implements Vehicle &#123;</span><br><span class="line">  id: Int!</span><br><span class="line">  manufacturer: String!</span><br><span class="line">  name: String!</span><br><span class="line">  flyLimit: String!</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
646
647<h3 id="Union"><a href="#Union" class="headerlink" title="Union"></a>Union</h3><p>Union은 각 타입을 묶어낼 수 있다는 점이 인터페이스ì
647™€ 비슷하나 공통 필드가 선언되지 않는다는 점에서 차이가 있다.<br>다음과 같이 선언한다.</p>
648<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">union SearchResult &#x3D; Album | Post | Todo</span><br></pre></td></tr></table></figure>
649
650<p>클라이언트에서는 마치 통합 검색과도 같이 사용할 수 있다.</p>
651<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20search(title%3A%20%22quos%22)%20%7B%0A%20%20%20%20...%20on%20Album%20%7B%0A%20%20%20%20%20%20title%0A%20%20%20%20%7D%0A%20%20%20%20...%20on%20Post%20%7B%0A%20%20%20%20%20%20title%0A%20%20%20%20%7D%0A%20%20%20%20...%20on%20Todo%20%7B%0A%20%20%20%20%20%20completed%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
652
653<h3 id="Input"><a href="#Input" class="headerlink" title="Input"></a>Input</h3><p>여태까지는 enum이나 String 등의 스칼라 타입만 인수로 사용했었으나, 더 복잡한 객체 또한 인수로 넘길 수 있다.<br>이 경우 아래와 같이 <code>Input</code> 타입을 정의할 수 있다.</p>
654<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">input CommentInput &#123;</span><br><span class="line">  name: String!</span><br><span class="line">  email: String!</span><br><span class="line">  body: String!</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
655
656<p>특정 Post에 댓글을 다는 변경을 다음과 같이 표현할 수 있겠다.</p>
657<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">mutation CreateComment($id: Int!, $comment: CommentInput) &#123;</span><br><span class="line">  createComment(id: $id, comment: $comment) &#123;</span><br><span class="line">    id</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"># Variable</span><br><span class="line">&#123;</span><br><span class="line">  &quot;id&quot;: 1,</span><br><span class="line">  &quot;comment&quot;: &#123;</span><br><span class="line">    &quot;name&quot;: &quot;댓글러&quot;,</span><br><span class="line">    &quot;email&quot;: &quot;댓글러@naver.com&quot;,</span><br><span class="line">    &quot;body&quot;: &quot;댓글&quot;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
658
659<h1 id="검증-Validation"><a href="#검증-Validation" class="headerlink" title="검증(Validation)"></a>검증(Validation)</h1><p>타입을 사용하기 때문에 GraphQL의 질의가 유효한지 여부를 미리 알 수 있다.<br>덕분에 서버와 클라이언트는 런타임으로 검증되기 전에 질의 생성 시점에서 유효하지 않은 질의를 알 수 있다.</p>
660<p>fragment는 자기 자신을 참조하지 못한다. 아래 구문은 오류가 발생한다.</p>
661<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  task &#123;</span><br><span class="line">    ...TitleAndSubTasks</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">fragment TitleAndSubTasks on Task &#123;</span><br><span class="line">  title</span><br><span class="line">  subTasks &#123;</span><br><span class="line">    ...TitleAndSubTasks</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
662
663<p>질의문은 해당 타입에 존재하는 필드만 선언해야 한다. 아래 구문은 오류가 발생한다.</p>
664<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20todo(id%3A1)%20%7B%0A%20%20%20%20undefiendField%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
665
666<p>또한 스칼라, enum 타입 외의 다른 타입을 질의한다면 반드시 하
666위 필드를 명시해야 한다.<br>아래 질의는 오류가 발생한다.</p>
667<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">  todo(id: 1)</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
668
669<h1 id="Introspection"><a href="#Introspection" class="headerlink" title="Introspection"></a>Introspection</h1><p>GraphQL은 Introspection System으로 어떤 스키마가 정의되어 있는지 확인할 수 있다.<br><code>__schema</code> 필드를 사용하여, 어떤 타입들이 사용가능한 지 알 수 있다.</p>
670<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20__schema%20%7B%0A%20%20%20%20types%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
671
672<p>꽤 많은 타입들을 발견할 수 있다. 다음과 같이 정리해보자.</p>
673<ul>
674<li>사용자 정의 객체 타입: Query, Todo, Post 등 개발자가 정의한 타입.</li>
675<li>스칼라 타입: String, Int, Boolean</li>
676<li>Introspection System Type: __Schema, __Type, __TypeKind, __Field, __InputValue, __EnumValue, __Diretive</li>
677</ul>
678<p>마지막에 다룬 부분이 GraphQL의 스키마를 엿볼 수 있는 Introspection의 타입들이 되겠다.</p>
679<p>아래와 같이 최초 진입점이었던 <code>Query</code>를 조회할 수 있다.</p>
680<blockquote>
681<p>GraphiQL에서 제공하는 우측 상단의 <code>&lt; Docs</code>와 비교해보자.</p>
682</blockquote>
683<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20__schema%20%7B%0A%20%20%20%20queryType%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%20%20fields%20%7B%0A%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D&variables=%7B%7D"></iframe>
684
685<p>특정 타입에 대해서는 <code>__type</code>으로 조회할 수 있다.</p>
686<iframe style="width: 100%" height="400px" resize="vertical" frameBorder="0" src="https://supawer-graphql-study.herokuapp.com/graphiql?query=%7B%0A%20%20__type(name%3A%20%22Post%22)%20%7B%0A%20%20%20%20name%0A%20%20%20%20kind%0A%20%20%20%20fields%20%7B%0A%20%20%20%20%20%20name%0A%20%20%20%20%20%20type%20%7B%0A%20%20%20%20%20%20%20%20ofType%20%7B%0A%20%20%20%20%20%20%20%20%20%20name%0A%20%20%20%20%20%20%20%20%20%20kind%0A%20%20%20%20%20%20%20%20%20%20description%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D%0A&variables=%7B%7D"></iframe>
687
688<p>이러한 Introspection의 도움으로 IDE나 브라우저에서 좋은 UX툴을 만들 수 있다.<br>이미 우리가 체험한 <a href="https://supawer-graphql-study.herokuapp.com/graphiql" target="_blank" rel="noopener">GraphiQL</a>도 마찬가지이고 <a href="https://supawer-graphql-study.herokuapp.com/voyager" target="_blank" rel="noopener">Voyager</a> 또한 문서화할 때 유용하다.</p>
689<h1 id="맺음말"><a href="#맺음말" class="headerlink" title="맺음말"></a>맺음말</h1><p>GraphQL에 대해서 기초적인 내용들을 익혀보았다.</p>
690<p>GraphQL은 타입 기반으로 스키마를 미리 정의한다.<br>클라이언트는 그 스키마를 바탕으로 질의문을 생성, 조작할 수 있다.<br>미리 정의된 스키마가 있으므로 질의에 사용된 인수에 대한 유효성 검사를 쉽게할 수 있다.<br>또한 Introspection System을 제공하고 있으므로 유용한 헬퍼 툴을 만들기 쉽고, 문서화하기도 편리하다.</p>
691<p>GraphQL을 어떻게 사용할 수 있는지는 알게 되었으나, 슬슬 백엔드에서는 어떻게 GraphQL을 구현했을 지 궁금할 즈음이다.<br>처음에는 체험하기, 기초, Spring Boot 연동의 3부작으로 포스트를 작성할 예정이었으나, 생각이 바뀌어 5부작이 될 것 같다.<br>체험하기, 기초, Best Practice, GraphQL Java, GraphQL On Spring Boot로 진행할 예정이다.</p>
692
693      
694    </div>
695    
696    
697    
698
699    
700
701    
702
703    
704      <div>
705        <ul class="post-copyright">
706  <li class="post-copyright-author">
707    <strong>작성자:</strong>
708    supawer0728
709  </li>
710  <li class="post-copyright-link">
711    <strong>원본링크:</strong>
712    <a href="https://supawer0728.github.io/2020/03/09/graphql-java2/" title="GraphQL Vol.2 - GraphQL 기초">https://supawer0728.github.io/2020/03/09/graphql-java2/</a>
713  </li>
714  <li class="post-copyright-license">
715    <strong>라이센스: </strong>
716    <a href="https://creativecommons.org/licenses/by-nc-sa/3.0/" rel="external nofollow" target="_blank">CC BY-NC-SA 3.0</a>
717  </li>
718</ul>
719
720      </div>
721    
722
723    <footer class="post-footer">
724      
725        <div class="post-tags">
726          
727            <a href="/tags/graphql/" rel="tag"># graphql</a>
728          
729        </div>
730      
731
732      
733      
734        <div class="post-widgets">
735        
736
737        
738
739        
740          
741          <div id="needsharebutton-postbottom">
742            <span class="btn">
743              <i class="fa fa-share-alt" aria-hidden="true"></i>
744            </span>
745          </div>
746        
747        </div>
748      
749      
750
751      
752        <div class="post-nav">
753          <div class="post-nav-next post-nav-item">
754            
755              <a href="/2020/03/07/graphql-java1/" rel="next" title="GraphQL Vol.1 - GraphQL 체험해보기">
756                <i class="fa fa-chevron-left"></i> GraphQL Vol.1 - GraphQL 체험해보기
757              </a>
758            
759          </div>
760
761          <span class="post-nav-divider"></span>
762
763          <div class="post-nav-prev post-nav-item">
764            
765          </div>
766        </div>
767      
768
769      
770      
771    </footer>
772  </div>
773  
774  
775  
776  </article>
777
778
779
780    <div class="post-spread">
781      
782    </div>
783  </div>
784
785
786          </div>
787          
788
789
790          
791
792  
793    <div class="comments" id="comments">
794      <div id="disqus_thread">
795        <noscript>
796          Please enable JavaScript to view the
797          <a href="https://disqus.com/?ref_noscript" target="_blank" rel="noopener">comments powered by Disqus.</a>
798        </noscript>
799      </div>
800    </div>
801
802  
803
804
805
806        </div>
807        
808          
809  
810  <div class="sidebar-toggle">
811    <div class="sidebar-toggle-line-wrap">
812      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
813      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
814      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
815    </div>
816  </div>
817
818  <aside id="sidebar" class="sidebar">
819    
820    <div class="sidebar-inner">
821
822      
823
824      
825        <ul class="sidebar-nav motion-element">
826          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
827            목차
828          </li>
829          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
830            흝어보기
831          </li>
832        </ul>
833      
834
835      <section class="site-overview-wrap sidebar-panel">
836        <div class="site-overview">
837          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
838            
839              <img class="site-author-image" itemprop="image"
840                src="/images/avatar.jpg"
841                alt="supawer0728" />
842            
843              <p class="site-author-name" itemprop="name">supawer0728</p>
844              <p class="site-description motion-element" itemprop="description"></p>
845          </div>
846
847          <nav class="site-state motion-element">
848
849            
850              <div class="site-state-item site-state-posts">
851              
852                <a href="/archives/%7C%7C%20archive">
853              
854                  <span class="site-state-item-count">34</span>
855                  <span class="site-state-item-name">포스트</span>
856                </a>
857              </div>
858            
859
860            
861              
862              
863              <div class="site-state-item site-state-categories">
864                <a href="/categories/index.html">
865                  <span class="site-state-item-count">17</span>
866                  <span class="site-state-item-name">카테고리</span>
867                </a>
868              </div>
869            
870
871            
872              
873              
874              <div class="site-state-item site-state-tags">
875                <a href="/tags/index.html">
876                  <span class="site-state-item-count">51</span>
877                  <span class="site-state-item-name">태그</span>
878                </a>
879              </div>
880            
881
882          </nav>
883
884          
885            <div class="feed-link motion-element">
886              <a href="/feed.xml" rel="alternate">
887                <i class="fa fa-rss"></i>
888                RSS
889              </a>
890            </div>
891          
892
893          
894            <div class="links-of-author motion-element">
895                
896                  <span class="links-of-author-item">
897                    <a href="https://github.com/supawer0728" target="_blank" title="GitHub">
898                      
899                        <i class="fa fa-fw fa-github"></i>GitHub</a>
900                  </span>
901                
902            </div>
903          
904
905          
906          
907
908          
909          
910
911          
912
913        </div>
914      </section>
915
916      
917      <!--noindex-->
918        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
919          <div class="post-toc">
920
921            
922              
923            
924
925            
926              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#머리말"><span class="nav-number">1.</span> <span class="nav-text">머리말</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#질의문-Query-변경문-Mutation"><span class="nav-number">2.</span> <span class="nav-text">질의문(Query) &#x2F; 변경문(Mutation)</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#필드"><span class="nav-number">2.0.1.</span> <span class="nav-text">필드</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#질의-인수-Arguments"><span class="nav-number">2.0.2.</span> <span class="nav-text">질의 인수(Arguments)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#별칭-할당-Aliases"><span class="nav-number">2.0.3.</span> <span class="nav-text">별칭 할당(Aliases)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Fragments"><span class="nav-number">2.0.4.</span> <span class="nav-text">Fragments</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#연산자명"><span class="nav-number">2.0.5.</span> <span class="nav-text">연산자명</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#변수-Variables"><span class="nav-number">2.0.6.</span> <span class="nav-text">변수(Variables)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#변수-정의"><span class="nav-number">2.0.7.</span> <span class="nav-text">변수 정의</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#변수-기본값"><span class="nav-number">2.0.8.</span> <span class="nav-text">변수 기본값</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#지시자-Directives"><span class="nav-number">2.0.9.</span> <span class="nav-text">지시자(Directives)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#변경문-Mutations"><span class="nav-number">2.0.10.</span> <span class="nav-text">변경문(Mutations)</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#mutation의-필드들"><span class="nav-number">2.0.11.</span> <span class="nav-text">mutation의 필드들</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Inline-Fragments"><span class="nav-number">2.0.12.</span> <span class="nav-text">Inline Fragments</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#메타-필드"><span class="nav-number">2.0.13.</span> <span class="nav-text">메타 필드</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#스키마와-타입"><span class="nav-number">3.</span> <span class="nav-text">스키마와 타입</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#타입"><span class="nav-number">3.0.1.</span> <span class="nav-text">타입</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#객체-타입과-필드"><span class="nav-number">
9263.0.2.</span> <span class="nav-text">객체 타입과 필드</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Query와-Mutation-타입"><span class="nav-number">3.0.3.</span> <span class="nav-text">Query와 Mutation 타입</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#스칼라-타입"><span class="nav-number">3.0.4.</span> <span class="nav-text">스칼라 타입</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Enumumeration"><span class="nav-number">3.0.5.</span> <span class="nav-text">Enumumeration</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#List와-Non-null"><span class="nav-number">3.0.6.</span> <span class="nav-text">List와 Non-null</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#인터페이스"><span class="nav-number">3.0.7.</span> <span class="nav-text">인터페이스</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Union"><span class="nav-number">3.0.8.</span> <span class="nav-text">Union</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#Input"><span class="nav-number">3.0.9.</span> <span class="nav-text">Input</span></a></li></ol></li></ol></li><li class="nav-item nav-level-1"><a class="nav-link" href="#검증-Validation"><span class="nav-number">4.</span> <span class="nav-text">검증(Validation)</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Introspection"><span class="nav-number">5.</span> <span class="nav-text">Introspection</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#맺음말"><span class="nav-number">6.</span> <span class="nav-text">맺음말</span></a></li></ol></div>
927            
928
929          </div>
930        </section>
931      <!--/noindex-->
932      
933
934      
935
936    </div>
937  </aside>
938
939
940        
941      </div>
942    </main>
943
944    <footer id="footer" class="footer">
945      <div class="footer-inner">
946        <div class="copyright">&copy; 2018 &mdash; <span itemprop="copyrightYear">2020</span>
947  <span class="with-love">
948    <i class="fa fa-heart"></i>
949  </span>
950  <span class="author" itemprop="copyrightHolder">supawer0728</span>
951
952  
953</div>
954
955
956  <div class="powered-by">Powered by <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a></div>
957
958
959
960  <span class="post-meta-divider">|</span>
961
962
963
964  <div class="theme-info">Theme &mdash; <a class="theme-link" target="_blank" href="https://github.com/iissnan/hexo-theme-next">NexT.Gemini</a> v5.1.4</div>
965
966
967
968  <div class="footer-custom">To Improve Human Life</div>
969
970
971        
972
973
974
975
976
977
978
979        
980      </div>
981    </footer>
982
983    
984      <div class="back-to-top">
985        <i class="fa fa-arrow-up"></i>
986        
987      </div>
988    
989
990    
991
992  </div>
993
994  
995
996<script type="text/javascript">
997  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
998    window.Promise = null;
999  }
1000</script>
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010  
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023  
1024  
1025    
1025<script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
1025
1026  
1027
1028  
1029  
1030    
1030<script type="text/javascript" src="/lib/fastclick/lib/fastclick.min.js?v=1.0.6"></script>
1030
1031  
1032
1033  
1034  
1035    
1035<script type="text/javascript" src="/lib/jquery_lazyload/jquery.lazyload.js?v=1.9.7"></script>
1035
1036  
1037
1038  
1039  
1040    
1040<script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
1040
1041  
1042
1043  
1044  
1045    
1045<script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
1045
1046  
1047
1048  
1049  
1050    
1050<script type="text/javascript" src="/lib/fancybox/source/jquery.fancybox.pack.js?v=2.1.5"></script>
1050
1051  
1052
1053
1054  
1055
1056
1057  
1057<script type="text/javascript" src="/js/src/utils.js?v=5.1.4"></script>
1057
1058
1059  
1059<script type="text/javascript" src="/js/src/motion.js?v=5.1.4"></script>
1059
1060
1061
1062
1063  
1064  
1065
1066
1067  
1067<script type="text/javascript" src="/js/src/affix.js?v=5.1.4"></script>
1067
1068
1069  
1069<script type="text/javascript" src="/js/src/schemes/pisces.js?v=5.1.4"></script>
1069
1070
1071
1072
1073  
1074  
1074<script type="text/javascript" src="/js/src/scrollspy.js?v=5.1.4"></script>
vendor: 1 bytes, line 1074
1074
1075<script type="text/javascript" src="/js/src/post-details.js?v=5.1.4"></script>
1075
1076
1077
1078
1079  
1080
1081
1082  
1082<script type="text/javascript" src="/js/src/bootstrap.js?v=5.1.4"></script>
1082
1083
1084
1085
1086  
1087
1088
1089  
1090
1091    
1092      
1092<script id="dsq-count-scr" src="https://supawer0728-github-com.disqus.com/count.js" async></script>
1092
1093    
1094
1095    
1096      
1096<script type="text/javascript">
1097        var disqus_config = function () {
1098          this.page.url = 'https://supawer0728.github.io/2020/03/09/graphql-java2/';
1099          this.page.identifier = '2020/03/09/graphql-java2/';
1100          this.page.title = 'GraphQL Vol.2 - GraphQL 기초';
1101        };
1102        var d = document, s = d.createElement('script');
1103        s.src = 'https://supawer0728-github-com.disqus.com/embed.js';
1104        s.setAttribute('data-timestamp', '' + +new Date());
1105        (d.head || d.body).appendChild(s);
1106      </script>
1106
1107    
1108
1109  
1110
1111
1112
1113
1114	
1115
1116
1117
1118
1119
1120  
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135  
1136
1137  
1137<script type="text/javascript">
1138    // Popup Window;
1139    var isfetched = false;
1140    var isXml = true;
1141    // Search DB path;
1142    var search_path = "search.xml";
1143    if (search_path.length === 0) {
1144      search_path = "search.xml";
1145    } else if (/json$/i.test(search_path)) {
1146      isXml = false;
1147    }
1148    var path = "/" + search_path;
1149    // monitor main search box;
1150
1151    var onPopupClose = function (e) {
1152      $('.popup').hide();
1153      $('#local-search-input').val('');
1154      $('.search-result-list').remove();
1155      $('#no-result').remove();
1156      $(".local-search-pop-overlay").remove();
1157      $('body').css('overflow', '');
1158    }
1159
1160    function proceedsearch() {
1161      $("body")
1162        .append('<div class="search-popup-overlay local-search-pop-overlay"></div>')
1163        .css('overflow', 'hidden');
1164      $('.search-popup-overlay').click(onPopupClose);
1165      $('.popup').toggle();
1166      var $localSearchInput = $('#local-search-input');
1167      $localSearchInput.attr("autocapitalize", "none");
1168      $localSearchInput.attr("autocorrect", "off");
1169      $localSearchInput.focus();
1170    }
1171
1172    // search function;
1173    var searchFunc = function(path, search_id, content_id) {
1174      'use strict';
1175
1176      // start loading animation
1177      $("body")
1178        .append('<div class="search-popup-overlay local-search-pop-overlay">' +
1179          '<div id="search-loading-icon">' +
1180          '<i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>' +
1181          '</div>' +
1182          '</div>')
1183        .css('overflow', 'hidden');
1184      $("#search-loading-icon").css('margin', '20% auto 0 auto').css('text-align', 'center');
1185
1186      $.ajax({
1187        url: path,
1188        dataType: isXml ? "xml" : "json",
1189        async: true,
1190        success: function(res) {
1191          // get the contents from search data
1192          isfetched = true;
1193          $('.popup').detach().appendTo('.header-inner');
1194          var datas = isXml ? $("entry", res).map(function() {
1195            return {
1196              title: $("title", this).text(),
1197              content: $("content",this).text(),
1198              url: $("url" , this).text()
1199            };
1200          }).get() : res;
1201          var input = document.getElementById(search_id);
1202          var resultContent = document.getElementById(content_id);
1203          var inputEventFunction = function() {
1204            var searchText = input.value.trim().toLowerCase();
1205            var keywords = searchText.split(/[\s\-]+/);
1206            if (keywords.length > 1) {
1207              keywords.push(searchText);
1208            }
1209            var resultItems = [];
1210            if (searchText.length > 0) {
1211              // perform local searching
1212              datas.forEach(function(data) {
1213                var isMatch = false;
1214                var hitCount = 0;
1215                var searchTextCount = 0;
1216                var title = data.title.trim();
1217                var titleInLowerCase = title.toLowerCase();
1218                var content = data.content.trim().replace(/<[^>]+>/g,"");
1219                var contentInLowerCase = content.toLowerCase();
1220                var articleUrl = decodeURIComponent(data.url);
1221                var indexOfTitle = [];
1222                var indexOfContent = [];
1223                // only match articles with not empty titles
1224                if(title != '') {
1225                  keywords.forEach(function(keyword) {
1226                    function getIndexByWord(word, text, caseSensitive) {
1227                      var wordLen = word.length;
1228                      if (wordLen === 0) {
1229                        return [];
1230                      }
1231                      var startPosition = 0, position = [], index = [];
1232                      if (!caseSensitive) {
1233                        text = text.toLowerCase();
1234                        word = word.toLowerCase();
1235                      }
1236                      while ((position = text.indexOf(word, startPosition)) > -1) {
1237                        index.push({position: position, word: word});
1238                        startPosition = position + wordLen;
1239                      }
1240                      return index;
1241                    }
1242
1243                    indexOfTitle = indexOfTitle.concat(getIndexByWord(keyword, titleInLowerCase, false));
1244                    indexOfContent = indexOfContent.concat(getIndexByWord(keyword, contentInLowerCase, false));
1245                  });
1246                  if (indexOfTitle.length > 0 || indexOfContent.length > 0) {
1247                    isMatch = true;
1248                    hitCount = indexOfTitle.length + indexOfContent.length;
1249                  }
1250                }
1251
1252                // show search results
1253
1254                if (isMatch) {
1255                  // sort index by position of keyword
1256
1257                  [indexOfTitle, indexOfContent].forEach(function (index) {
1258                    index.sort(function (itemLeft, itemRight) {
1259                      if (itemRight.position !== itemLeft.position) {
1260                        return itemRight.position - itemLeft.position;
1261                      } else {
1262                        return itemLeft.word.length - itemRight.word.length;
1263                      }
1264                    });
1265                  });
1266
1267                  // merge hits into slices
1268
1269                  function mergeIntoSlice(text, start, end, index) {
1270                    var item = index[index.length - 1];
1271                    var position = item.position;
1272                    var word = item.word;
1273                    var hits = [];
1274                    var searchTextCountInSlice = 0;
1275                    while (position + word.length <= end && index.length != 0) {
1276                      if (word === searchText) {
1277                        searchTextCountInSlice++;
1278                      }
1279                      hits.push({position: position, length: word.length});
1280                      var wordEnd = position + word.length;
1281
1282                      // move to next position of hit
1283
1284                      index.pop();
1285                      while (index.length != 0) {
1286                        item = index[index.length - 1];
1287                        position = item.position;
1288                        word = item.word;
1289                        if (wordEnd > position) {
1290                          index.pop();
1291                        } else {
1292                          break;
1293                        }
1294                      }
1295                    }
1296                    searchTextCount += searchTextCountInSlice;
1297                    return {
1298                      hits: hits,
1299                      start: start,
1300                      end: end,
1301                      searchTextCount: searchTextCountInSlice
1302                    };
1303                  }
1304
1305                  var slicesOfTitle = [];
1306                  if (indexOfTitle.length != 0) {
1307                    slicesOfTitle.push(mergeIntoSlice(title, 0, title.length, indexOfTitle));
1308                  }
1309
1310                  var slicesOfContent = [];
1311                  while (indexOfContent.length != 0) {
1312                    var item = indexOfContent[indexOfContent.length - 1];
1313                    var position = item.position;
1314                    var word = item.word;
1315                    // cut out 100 characters
1316                    var start = position - 20;
1317                    var end = position + 80;
1318                    if(start < 0){
1319                      start = 0;
1320                    }
1321                    if (end < position + word.length) {
1322                      end = position + word.length;
1323                    }
1324                    if(end > content.length){
1325                      end = content.length;
1326                    }
1327                    slicesOfContent.push(mergeIntoSlice(content, start, end, indexOfContent));
1328                  }
1329
1330                  // sort slices in content by search text's count and hits' count
1331
1332                  slicesOfContent.sort(function (sliceLeft, sliceRight) {
1333                    if (sliceLeft.searchTextCount !== sliceRight.searchTextCount) {
1334                      return sliceRight.searchTextCount - sliceLeft.searchTextCount;
1335                    } else if (sliceLeft.hits.length !== sliceRight.hits.length) {
1336                      return sliceRight.hits.length - sliceLeft.hits.length;
1337                    } else {
1338                      return sliceLeft.start - sliceRight.start;
1339                    }
1340                  });
1341
1342                  // select top N slices in content
1343
1344                  var upperBound = parseInt('-1');
1345                  if (upperBound >= 0) {
1346                    slicesOfContent = slicesOfContent.slice(0, upperBound);
1347                  }
1348
1349                  // highlight title and content
1350
1351                  function highlightKeyword(text, slice) {
1352                    var result = '';
1353                    var prevEnd = slice.start;
1354                    slice.hits.forEach(function (hit) {
1355                      result += text.substring(prevEnd, hit.position);
1356                      var end = hit.position + hit.length;
1357                      result += '<b class="search-keyword">' + text.substring(hit.position, end) + '</b>';
1358                      prevEnd = end;
1359                    });
1360                    result += text.substring(prevEnd, slice.end);
1361                    return result;
1362                  }
1363
1364                  var resultItem = '';
1365
1366                  if (slicesOfTitle.length != 0) {
1367                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + highlightKeyword(title, slicesOfTitle[0]) + "</a>";
1368                  } else {
1369                    resultItem += "<li><a href='" + articleUrl + "' class='search-result-title'>" + title + "</a>";
1370                  }
1371
1372                  slicesOfContent.forEach(function (slice) {
1373                    resultItem += "<a href='" + articleUrl + "'>" +
1374                      "<p class=\"search-result\">" + highlightKeyword(content, slice) +
1375                      "...</p>" + "</a>";
1376                  });
1377
1378                  resultItem += "</li>";
1379                  resultItems.push({
1380                    item: resultItem,
1381                    searchTextCount: searchTextCount,
1382                    hitCount: hitCount,
1383                    id: resultItems.length
1384                  });
1385                }
1386              })
1387            };
1388            if (keywords.length === 1 && keywords[0] === "") {
1389              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-search fa-5x" /></div>'
1390            } else if (resultItems.length === 0) {
1391              resultContent.innerHTML = '<div id="no-result"><i class="fa fa-frown-o fa-5x" /></div>'
1392            } else {
1393              resultItems.sort(function (resultLeft, resultRight) {
1394                if (resultLeft.searchTextCount !== resultRight.searchTextCount) {
1395                  return resultRight.searchTextCount - resultLeft.searchTextCount;
1396                } else if (resultLeft.hitCount !== resultRight.hitCount) {
1397                  return resultRight.hitCount - resultLeft.hitCount;
1398                } else {
1399                  return resultRight.id - resultLeft.id;
1400                }
1401              });
1402              var searchResultList = '<ul class=\"search-result-list\">';
1403              resultItems.forEach(function (result) {
1404                searchResultList += result.item;
1405              })
1406              searchResultList += "</ul>";
1407              resultContent.innerHTML = searchResultList;
1408            }
1409          }
1410
1411          if ('auto' === 'auto') {
1412            input.addEventListener('input', inputEventFunction);
1413          } else {
1414            $('.search-icon').click(inputEventFunction);
1415            input.addEventListener('keypress', function (event) {
1416              if (event.keyCode === 13) {
1417                inputEventFunction();
1418              }
1419            });
1420          }
1421
1422          // remove loading animation
1423          $(".local-search-pop-overlay").remove();
1424          $('body').css('overflow', '');
1425
1426          proceedsearch();
1427        }
1428      });
1429    }
1430
1431    // handle and trigger popup window;
1432    $('.popup-trigger').click(function(e) {
1433      e.stopPropagation();
1434      if (isfetched === false) {
1435        searchFunc(path, 'local-search-input', 'local-search-result');
1436      } else {
1437        proceedsearch();
1438      };
1439    });
1440
1441    $('.popup-btn-close').click(onPopupClose);
1442    $('.popup').click(function(e){
1443      e.stopPropagation();
1444    });
1445    $(document).on('keyup', function (event) {
1446      var shouldDismissSearchPopup = event.which === 27 &&
1447        $('.search-popup').is(':visible');
1448      if (shouldDismissSearchPopup) {
1449        onPopupClose();
1450      }
1451    });
1452  </script>
1452
1453
1454
1455
1456
1457
1458  
1459
1460  
1461
1462  
1463
1464  
1465  
1466  
1467  <link rel="stylesheet" href="/lib/needsharebutton/needsharebutton.css">
1468
1469  
1470  
1471  
1471<script src="/lib/needsharebutton/needsharebutton.js"></script>
1471
1472
1473  
1473<script>
1474    
1475      pbOptions = {};
1476      
1477          pbOptions.iconStyle = "box";
1478      
1479          pbOptions.boxForm = "horizontal";
1480      
1481          pbOptions.position = "bottomCenter";
1482      
1483          pbOptions.networks = "Twitter,Facebook,GooglePlus,Evernote";
1484      
1485      new needShareButton('#needsharebutton-postbottom', pbOptions);
1486    
1487    
1488  </script>
1488
1489
1490  
1491
1492  
1493
1494  
1495
1496  
1497
1498</body>
1499</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.