@@ -35,10 +35,10 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
3535 <div class="flex flex-col md:flex-row gap-4 mb-6">
3636 <div class="flex-1 max-w-md">
3737 <div class="relative">
38- <input
39- type="text"
40- [(ngModel)]="searchTerm"
41- (input)="filterApis()"
38+ <input
39+ type="text"
40+ [(ngModel)]="searchTerm"
41+ (input)="filterApis()"
4242 placeholder="경로나 설명으로 검색..."
4343 class="w-full p-3 pr-12 bg-md-sys-color-surface-container-highest text-md-sys-color-on-surface rounded-xl border border-md-sys-color-outline focus:border-md-sys-color-primary focus:outline-none md-typescale-body-large">
4444 <mat-icon class="w-5 h-5 absolute right-3 top-1/2 transform -translate-y-1/2 text-md-sys-color-on-surface-variant">search</mat-icon>
@@ -84,18 +84,18 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
8484 <div *ngFor="let doc of filteredDocs; trackBy: trackByPath" class="md-card bg-md-sys-color-surface-container text-md-sys-color-on-surface">
8585 <div class="flex items-center justify-between mb-4">
8686 <div class="flex items-center gap-3">
87- <span class="px-3 py-1 rounded-full text-sm font-medium min-w-[60px] text-center"
88- [class]="getMethodColor(doc.method) === 'primary' ? 'bg-md-sys-color-primary text-md-sys-color-on-primary' :
89- getMethodColor(doc.method) === 'accent' ? 'bg-md-sys-color-secondary text-md-sys-color-on-secondary' :
90- getMethodColor(doc.method) === 'warn' ? 'bg-md-sys-color-error text-md-sys-color-on-error' :
87+ <span class="px-3 py-1 rounded-full text-sm font-medium min-w-[60px] text-center"
88+ [class]="getMethodColor(doc.method) === 'primary' ? 'bg-md-sys-color-primary text-md-sys-color-on-primary' :
89+ getMethodColor(doc.method) === 'accent' ? 'bg-md-sys-color-secondary text-md-sys-color-on-secondary' :
90+ getMethodColor(doc.method) === 'warn' ? 'bg-md-sys-color-error text-md-sys-color-on-error' :
9191 'bg-md-sys-color-surface-container-high text-md-sys-color-on-surface'">
9292 {{ doc.method }}
9393 </span>
9494 <span class="font-mono md-typescale-title-medium font-medium text-md-sys-color-on-surface">{{ doc.path }}</span>
9595 </div>
9696 </div>
97-
98- <p class="md-typescale-body-large text-md-sys-color-on-surface-variant mb-6">{{ doc.brief }}</p>
97+
98+ <p class="md-typescale-body-large text-md-sys-color-on-surface-variant mb-6">{{ doc.name }}</p>
9999
100100 <div class="space-y-6">
101101 <div class="grid grid-cols-1 md:grid-cols-3 gap-4 p-4 bg-md-sys-color-surface-container-high rounded-xl">
@@ -105,10 +105,10 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
105105 </div>
106106 <div class="flex items-center gap-2">
107107 <span class="md-typescale-body-medium font-medium text-md-sys-color-on-surface-variant">메소드:</span>
108- <span class="px-2 py-1 rounded-full text-xs"
109- [class]="getMethodColor(doc.method) === 'primary' ? 'bg-md-sys-color-primary-container text-md-sys-color-on-primary-container' :
110- getMethodColor(doc.method) === 'accent' ? 'bg-md-sys-color-secondary-container text-md-sys-color-on-secondary-container' :
111- getMethodColor(doc.method) === 'warn' ? 'bg-md-sys-color-error-container text-md-sys-color-on-error-container' :
108+ <span class="px-2 py-1 rounded-full text-xs"
109+ [class]="getMethodColor(doc.method) === 'primary' ? 'bg-md-sys-color-primary-container text-md-sys-color-on-primary-container' :
110+ getMethodColor(doc.method) === 'accent' ? 'bg-md-sys-color-secondary-container text-md-sys-color-on-secondary-container' :
111+ getMethodColor(doc.method) === 'warn' ? 'bg-md-sys-color-error-container text-md-sys-color-on-error-container' :
112112 'bg-md-sys-color-surface-container text-md-sys-color-on-surface'">
113113 {{ doc.method }}
114114 </span>
@@ -119,12 +119,43 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
119119 </div>
120120 </div>
121121
122- <div *ngIf="doc.details " class="space-y-2">
122+ <div *ngIf="doc.routeParams && doc.routeParams.length > 0 " class="space-y-2">
123123 <h4 class="md-typescale-title-small text-md-sys-color-on-surface flex items-center gap-2">
124- <mat-icon class="w-5 h-5 text-md-sys-color-primary">description </mat-icon>
125- 상세 설명
124+ <mat-icon class="w-5 h-5 text-md-sys-color-primary">route </mat-icon>
125+ 경로 파라미터
126126 </h4>
127- <p class="md-typescale-body-medium text-md-sys-color-on-surface leading-relaxed">{{ doc.details }}</p>
127+ <div class="space-y-3">
128+ <div *ngFor="let routeParam of doc.routeParams" class="p-3 bg-md-sys-color-primary-container rounded-lg">
129+ <div class="flex items-start justify-between mb-2">
130+ <div class="flex items-center gap-2">
131+ <code class="px-2 py-1 bg-md-sys-color-surface-container rounded text-sm font-mono text-md-sys-color-on-surface">:{{ routeParam.name }}</code>
132+ <span class="px-2 py-1 rounded-full text-xs bg-md-sys-color-error-container text-md-sys-color-on-error-container">필수</span>
133+ </div>
134+ <span class="text-sm font-mono text-md-sys-color-on-primary-container">{{ routeParam.type }}</span>
135+ </div>
136+ <p class="md-typescale-body-small text-md-sys-color-on-primary-container">{{ routeParam.description }}</p>
137+ </div>
138+ </div>
139+ </div>
140+
141+ <div *ngIf="doc.params && doc.params.length > 0" class="space-y-2">
142+ <h4 class="md-typescale-title-small text-md-sys-color-on-surface flex items-center gap-2">
143+ <mat-icon class="w-5 h-5 text-md-sys-color-primary">settings</mat-icon>
144+ {{ doc.method.toUpperCase() === 'GET' ? '쿼리 파라미터' : '요청 본문' }}
145+ </h4>
146+ <div class="space-y-3">
147+ <div *ngFor="let param of doc.params" class="p-3 bg-md-sys-color-surface-container-high rounded-lg">
148+ <div class="flex items-start justify-between mb-2">
149+ <div class="flex items-center gap-2">
150+ <code class="px-2 py-1 bg-md-sys-color-surface-container rounded text-sm font-mono text-md-sys-color-on-surface">{{ param.name }}</code>
151+ <span class="px-2 py-1 rounded-full text-xs"
152+ [class]="param.required ? 'bg-md-sys-color-error-container text-md-sys-color-on-error-container' : 'bg-md-sys-color-surface-container text-md-sys-color-on-surface'">{{ param.required ? '필수' : '선택' }}</span>
153+ </div>
154+ <span class="text-sm font-mono text-md-sys-color-on-surface-variant">{{ param.type }}</span>
155+ </div>
156+ <p class="md-typescale-body-small text-md-sys-color-on-surface-variant">{{ param.description }}</p>
157+ </div>
158+ </div>
128159 </div>
129160
130161 <div *ngIf="doc.returns" class="space-y-2">
@@ -142,8 +173,8 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
142173 </h4>
143174 <div class="relative bg-gray-900 text-gray-100 rounded-xl overflow-hidden">
144175 <pre class="p-4 overflow-x-auto font-mono text-sm leading-relaxed"><code>{{ generateCurlExample(doc) }}</code></pre>
145- <button class="absolute top-3 right-3 p-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"
146- (click)="copyCurl(doc)"
176+ <button class="absolute top-3 right-3 p-2 bg-gray-800 hover:bg-gray-700 rounded-lg transition-colors"
177+ (click)="copyCurl(doc)"
147178 title="클립보드에 복사">
148179 <mat-icon class="w-5 h-5 text-gray-300">content_copy</mat-icon>
149180 </button>
@@ -161,11 +192,11 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
161192 border-radius: 16px;
162193 transition: all 0.3s ease;
163194 }
164-
195+
165196 .md-card:hover {
166197 transform: translateY(-2px);
167198 }
168-
199+
169200 .md-button {
170201 border: none;
171202 cursor: pointer;
@@ -175,17 +206,17 @@ import { AdminService, ApiDoc } from '../../services/admin.service';
175206 align-items: center;
176207 justify-content: center;
177208 }
178-
209+
179210 .md-button:hover {
180211 transform: translateY(-1px);
181212 }
182-
213+
183214 .md-button:disabled {
184215 opacity: 0.5;
185216 cursor: not-allowed;
186217 transform: none;
187218 }
188-
219+
189220 input:focus {
190221 box-shadow: 0 0 0 2px var(--md-sys-color-primary);
191222 }
@@ -203,6 +234,7 @@ export class ApiDocsComponent implements OnInit {
203234 this . loadApiDocs ( ) ;
204235 }
205236
237+
206238 loadApiDocs ( ) {
207239 this . loading = true ;
208240 this . adminService . getApiDocs ( ) . subscribe ( {
@@ -234,10 +266,17 @@ export class ApiDocsComponent implements OnInit {
234266 const term = this . searchTerm . toLowerCase ( ) ;
235267 this . filteredDocs = this . apiDocs . filter ( doc =>
236268 doc . path . toLowerCase ( ) . includes ( term ) ||
237- doc . brief . toLowerCase ( ) . includes ( term ) ||
238- doc . details . toLowerCase ( ) . includes ( term ) ||
269+ doc . name . toLowerCase ( ) . includes ( term ) ||
239270 doc . method . toLowerCase ( ) . includes ( term ) ||
240- doc . file . toLowerCase ( ) . includes ( term )
271+ doc . file . toLowerCase ( ) . includes ( term ) ||
272+ ( doc . params && doc . params . some ( param =>
273+ param . name . toLowerCase ( ) . includes ( term ) ||
274+ param . description . toLowerCase ( ) . includes ( term )
275+ ) ) ||
276+ ( doc . routeParams && doc . routeParams . some ( routeParam =>
277+ routeParam . name . toLowerCase ( ) . includes ( term ) ||
278+ routeParam . description . toLowerCase ( ) . includes ( term )
279+ ) )
241280 ) ;
242281 }
243282
@@ -277,31 +316,65 @@ export class ApiDocsComponent implements OnInit {
277316
278317 generateCurlExample ( doc : ApiDoc ) : string {
279318 const baseUrl = 'https://api-dev.dimiplan.com' ;
280- const url = `${ baseUrl } ${ doc . path } ` ;
319+ let url = `${ baseUrl } ${ doc . path } ` ;
320+
321+ // Replace route parameters with example values
322+ if ( doc . routeParams && doc . routeParams . length > 0 ) {
323+ doc . routeParams . forEach ( routeParam => {
324+ const exampleValue = routeParam . type === 'string' ? 'example' : routeParam . type === 'number' ? '123' : 'value' ;
325+ url = url . replace ( `:${ routeParam . name } ` , exampleValue ) ;
326+ } ) ;
327+ }
281328
282329 switch ( doc . method . toUpperCase ( ) ) {
283330 case 'GET' :
331+ if ( doc . params && doc . params . length > 0 ) {
332+ const queryParams = doc . params . map ( param => `${ param . name } =${ param . type === 'string' ? 'value' : param . type === 'number' ? '1' : 'true' } ` ) . join ( '&' ) ;
333+ url += `?${ queryParams } ` ;
334+ }
284335 return `curl -X GET "${ url } " \\
285336 -H "Content-Type: application/json" \\
286337 -H "Authorization: Bearer YOUR_TOKEN"` ;
287338
288339 case 'POST' :
340+ let postBody = '{\n' ;
341+ if ( doc . params && doc . params . length > 0 ) {
342+ const bodyParams = doc . params . map ( param => {
343+ const value = param . type === 'string' ? '"value"' : param . type === 'number' ? '1' : 'true' ;
344+ return ` "${ param . name } ": ${ value } ` ;
345+ } ) . join ( ',\n' ) ;
346+ postBody += bodyParams + '\n' ;
347+ } else {
348+ postBody += ' "key": "value"\n' ;
349+ }
350+ postBody += ' }' ;
289351 return `curl -X POST "${ url } " \\
290352 -H "Content-Type: application/json" \\
291353 -H "Authorization: Bearer YOUR_TOKEN" \\
292- -d '{
293- "key": "value"
294- }'` ;
354+ -d '${ postBody } '` ;
295355
296356 case 'PUT' :
357+ let putBody = '{\n' ;
358+ if ( doc . params && doc . params . length > 0 ) {
359+ const bodyParams = doc . params . map ( param => {
360+ const value = param . type === 'string' ? '"value"' : param . type === 'number' ? '1' : 'true' ;
361+ return ` "${ param . name } ": ${ value } ` ;
362+ } ) . join ( ',\n' ) ;
363+ putBody += bodyParams + '\n' ;
364+ } else {
365+ putBody += ' "key": "value"\n' ;
366+ }
367+ putBody += ' }' ;
297368 return `curl -X PUT "${ url } " \\
298369 -H "Content-Type: application/json" \\
299370 -H "Authorization: Bearer YOUR_TOKEN" \\
300- -d '{
301- "key": "value"
302- }'` ;
371+ -d '${ putBody } '` ;
303372
304373 case 'DELETE' :
374+ if ( doc . params && doc . params . length > 0 ) {
375+ const queryParams = doc . params . map ( param => `${ param . name } =${ param . type === 'string' ? 'value' : param . type === 'number' ? '1' : 'true' } ` ) . join ( '&' ) ;
376+ url += `?${ queryParams } ` ;
377+ }
305378 return `curl -X DELETE "${ url } " \\
306379 -H "Content-Type: application/json" \\
307380 -H "Authorization: Bearer YOUR_TOKEN"` ;
@@ -351,14 +424,25 @@ export class ApiDocsComponent implements OnInit {
351424
352425 for ( const doc of docs ) {
353426 markdown += `### ${ doc . method } ${ doc . path } \n\n` ;
354- if ( doc . brief ) {
355- markdown += `**요약:** ${ doc . brief } \n\n` ;
427+ if ( doc . name ) {
428+ markdown += `**요약:** ${ doc . name } \n\n` ;
429+ }
430+ if ( doc . routeParams && doc . routeParams . length > 0 ) {
431+ markdown += `**경로 파라미터:**\n\n` ;
432+ for ( const routeParam of doc . routeParams ) {
433+ markdown += `- \`:${ routeParam . name } \` (${ routeParam . type } ) **필수**: ${ routeParam . description } \n` ;
434+ }
435+ markdown += '\n' ;
356436 }
357- if ( doc . details ) {
358- markdown += `**설명:** ${ doc . details } \n\n` ;
437+ if ( doc . params && doc . params . length > 0 ) {
438+ markdown += `**${ doc . method . toUpperCase ( ) === 'GET' ? '쿼리 파라미터' : '요청 본문' } :**\n\n` ;
439+ for ( const param of doc . params ) {
440+ markdown += `- \`${ param . name } \` (${ param . type } ) ${ param . required ? '**필수**' : '*선택*' } : ${ param . description } \n` ;
441+ }
442+ markdown += '\n' ;
359443 }
360444 if ( doc . returns ) {
361- markdown += `**반환값:** ${ doc . returns } \n\n` ;
445+ markdown += `**반환값:** ${ doc . returns . type } - ${ doc . returns . description } \n\n` ;
362446 }
363447 markdown += '**사용 예시:**\n\n' ;
364448 markdown += '```bash\n' ;
@@ -371,7 +455,7 @@ export class ApiDocsComponent implements OnInit {
371455 return markdown ;
372456 }
373457
374- trackByPath ( index : number , doc : ApiDoc ) : string {
458+ trackByPath ( _ : number , doc : ApiDoc ) : string {
375459 return `${ doc . method } -${ doc . path } ` ;
376460 }
377461}
0 commit comments