Dev/Project

[Spring&Angular] Spring 게시판 만들기 - Angular와 Swagger 연동

Mr.Walker 2020. 3. 19. 20:22
반응형

!주의

Spring 게시판 만들기 포스트는 개발 기록을 남기는 것에 의의가 있습니다.

포스팅되는 내용대로 꼭 해야 한다는 법은 없습니다.

이 포스트는 이전 포스트에서 이어집니다.

해당 포스트는 개념보다 실전 위주로 작성되어 자세한 설명은 다루지 않습니다.

 

 

이번 프로젝트의 핵심인 Angular와 Swagger의 연동에 대해서 다뤄보겠습니다.

먼저 실전위주로 작성되어 있기 때문에 개념이나 자세한 설명은 추후에 보강하겠습니다.

 

이번 프로젝트에 작성할 기능을 파일을 담을 디렉토리 features를 생성합니다.

생성하는 방법은 아래와 같습니다.

 

먼저 API에 대응하는 모델을 만들어줄 필요가 있습니다.

features 디렉토리 안에 board.model.ts를 작성합니다.

//board.model.ts
export class BoardModel{
  bno : number;
  subject : string;
  reg_date : string;
}

export class BoardConDto{
  reg_date : string;
  bno : number;
  subject : string;
}

board.model.ts의 class의 객체를 다른 파일에서 사용하기위해서 export로 클래스를 작성합니다.

 

environments 디렉토리에 있는 environments.ts에 하단의 코드를 추가합니다.

export const environment = {
  production: false,
  demo: {
    api: "http://localhost:8080/v1/"
  }
};

 

board.model.ts에서 BoardConDto를 만들었으니 이번에는 boardService.ts를 작성합니다.

//board.service.ts
import {environment} from "../../environments/environment";
import {HttpClient, HttpHeaders} from "@angular/common/http";
import {Injectable} from "@angular/core";
import {Observable} from "rxjs";
import {map} from "rxjs/operators";


const endpoint = environment.demo.api;

const httpOptions = {
  headers: new HttpHeaders({
    "Content-Type": "application/json"
  })
};


@Injectable({
  providedIn: "root"
})


export class BoardService {

  constructor(private http: HttpClient) {
  }

  private static extractData(res: Response) {
    return res || {};
  }

  boardInsert(bno): Observable<any> {
    return this.http.put(endpoint + "board", bno).pipe(map(BoardService.extractData));
  }

  boardList(BoardConDto): Observable<any> {
    return this.http.post(endpoint + "board", BoardConDto, httpOptions).pipe(map(BoardService.extractData));
  }

  boardList2(): Observable<any> {
    return this.http.get<any>(endpoint + "board", httpOptions).pipe(map(BoardService.extractData));
  }

  boardDelete(bno): Observable<any> {
    return this.http.delete(endpoint + "board/{bno}" + bno).pipe(map(BoardService.extractData));
  }

}

 

boardService.ts를 작성했으니 이제 board.Component.ts를 작성하겠습니다.

import {Component, OnInit, ViewChild} from "@angular/core";
import {ColumnMode, SelectionType} from "@swimlane/ngx-datatable";
import {FormGroup} from "@angular/forms";
import {BoardService} from "./board.service";
import {DatePipe} from "@angular/common";
import {BoardConDto, BoardModel} from "./board.model";

@Component({
  selector: "app-demo-board",
  templateUrl: "./board.component.html"
})

export class BoardComponent implements OnInit {

  board: BoardModel;

  boardRows = [];
  ColumnMode = ColumnMode;
  SelectionType = SelectionType;
  selected = [];
  temp = [];
  pageSize = 10;
  columns = [
    {prop: "bno", name: "게시판번호"},
    {prop: "boardDe", name: "공지일자"},
    {prop: "subject", name: "공지제목"},
  ];
  @ViewChild("boardForm", {static: true}) boardForm: FormGroup;

  public boardConDto = new BoardConDto();

  constructor(private rest: BoardService) {
    this.rest.boardList2().subscribe(data => {
        console.log("121", data);
      }
    );
  }

  ngOnInit(): void {
    console.log("test");
    this.board = new BoardModel();
    const endDate = new Date();
    const startDate = new Date();
    startDate.setDate(startDate.getDate() -7);
    const dataPipe = new DatePipe("en-us");
    this.boardConDto.reg_date = dataPipe.transform(startDate, "yyyy-MM-dd").toString();
  }

  retrieve() {
    const board: BoardConDto = new BoardConDto();
    console.log(board);

    this.rest.boardList2().subscribe(data => {
        this.temp = [...data];
        this.boardRows = data;
      }
    );
  }

  new() {
    this.board = new BoardModel();
  }

  save() {
    console.log(this.board);
    if (this.board.subject !== "" && this.board.subject != null) {
      this.rest.boardInsert(this.board).subscribe(
        data => {
          console.log("게시글 저장", "정보 저장 성공");
          // this.notify.show("게시글 저장" + data.message, "성공");
        },
        data => {
          console.log("게시글 저장", data.error.message);
          // this.notify.show("게시글 저장", "실패");
        },
        () => {
          console.log("게시글 저장", "완료");
          this.board = new BoardModel();
          this.boardForm.reset();
        }
      );
    }
  }

  delete() {
    if (this.board.bno != null) {
      this.rest.boardDelete(this.board.bno).subscribe(
        data => {
          console.log("게시글 삭제", data.message);
        },
        data => {
          // console.log(data);
          console.log("게시글 삭제", data.error.message);
        },
        () => console.log("게시글 삭제", "완료")
      );
    }
  }

  onSelect({selected}) {
    // console.log('Select Event', selected, this.selected);
    this.board = selected[0];

  }

}

 

app-routing.module.ts를 /app아래에 생성 후 작성합니다.

import {NgModule} from "@angular/core";
import {Routes, RouterModule} from "@angular/router";
import {BoardComponent} from "./features/board.component";

const routes: Routes = [
  {path: "board", component: BoardComponent}
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

 

app.module.ts를 아래와 같이 수정합니다.

import {BrowserModule} from "@angular/platform-browser";
import {NgModule} from "@angular/core";

import {AppRoutingModule} from "./app-routung.module";
import {AppComponent} from "./app.component";
import {BoardComponent} from "./features/sysMgmt/board/board.component";
import {HttpClientModule} from "@angular/common/http";
import {NgxDatatableModule} from "@swimlane/ngx-datatable";
import {FormsModule} from "@angular/forms";

@NgModule({
  declarations: [
    AppComponent,
    BoardComponent

  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule,
    NgxDatatableModule,
    FormsModule
  ],
  exports: [
  ],
  providers: [
  ],
  bootstrap: [AppComponent]
})
export class AppModule {
}

app.component.html 안에 있는 내용을 지운 후 아래의 코드를 넣습니다. //19.03.20 추가

<div class="wrapper">
  <a mat-list-item [routerLink]="['board']">
    <span class="label">게시판</span>
  </a>
  <app-demo-board></app-demo-board>

 

 

그리고 board.component.html을 일단 API가 연동되는 것을 확인하기 위해 버튼을 만들어

retrieve()메소드와 연결하는 코드를 작성합니다. //19.03.20 추가

<!doctype html>
<html lang="ko">
<head>
  <meta charset = "UTF-8">
</head>
<body>
<button (click)="retrieve()"></button>

</body>
</html>

 

그 후 Run을 통해서 하단의 주소에서 버튼을 클릭해 결과를 개발자도구 network의 board에서 확인이 가능합니다.

http://localhost:4200/

board의 Header와 Response을 통해 API와 연동이 정상적으로 이루어지는 것을 알 수 있습니다.

 

!CORS 에러로 인해 연동이 되지 않을 수도 있습니다.

build.gradle의 dependencies에 아래의 코드를 추가한 후 Reimport를 진행합니다.

 implementation 'org.apache.httpcomponents:httpclient'

 

그 후 BoardController.java에서 아래와 같은 방식으로 코드를 추가해서 해결할 수 있습니다.

    @ApiOperation(value = "게시판 조회", notes = "게시판 조회")
    @CrossOrigin(origins = "http://localhost:4200")
    @PostMapping(value = "/board")

@CrossOrigin 어노테이션을 @ApiOperation과 각 매핑된 어노테이션 사이에 추가하여 연동할 수 있습니다.

하지만 어디까지나 이 방법은 임시방편일뿐 추후에 CORS 설정을 API에서 추가해야합니다.

반응형