CRUD 작업(생성, 읽기, 업데이트, 삭제)은 대부분의 웹 애플리케이션의 백본입니다. 이 튜토리얼에서는 프런트엔드에 Angular를 사용하고 백엔드에 GoAPI를 사용하여 CRUD 앱을 구축하여 완전히 통합되고 효율적인 전체 스택 솔루션을 만드는 방법을 보여줍니다.
Angular 18을 설치하고 다음 명령을 사용하여 새 프로젝트를 만듭니다.
npm install -g @angular/cli@18.0.0 ng new view --minimal --routing --style css --no-standalone --ssr=false
└─ src ├─ app │ ├─ app-routing.module.ts │ ├─ app.component.ts │ ├─ app.interceptor.ts │ ├─ app.module.ts │ └─ components │ └─ product │ ├─ Create.component.ts │ ├─ Delete.component.ts │ ├─ Detail.component.ts │ ├─ Edit.component.ts │ ├─ Index.component.ts │ └─ Product.service.ts ├─ index.html ├─ main.ts └─ styles.css
*이 프로젝트 구조에는 생성하거나 수정하려는 파일과 폴더만 표시됩니다.
import { enableProdMode } from '@angular/core' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { AppModule } from './app/app.module' platformBrowserDynamic().bootstrapModule(AppModule).catch(e => console.error(e))
이 main.ts 파일은 platformBrowserDynamic 함수를 사용하여 AppModule을 부트스트랩하여 Angular 애플리케이션을 초기화합니다. 브라우저에서 실행되도록 애플리케이션을 설정하고 부트스트랩 프로세스 중에 발생하는 모든 오류를 처리합니다.
import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' import { AppInterceptor } from './app.interceptor' import { ProductIndex } from './components/product/Index.component' import { ProductCreate } from './components/product/Create.component' import { ProductDetail } from './components/product/Detail.component' import { ProductEdit } from './components/product/Edit.component' import { ProductDelete } from './components/product/Delete.component' @NgModule({ declarations: [ AppComponent, ProductIndex, ProductCreate, ProductDetail, ProductEdit, ProductDelete, ], imports: [ BrowserModule, AppRoutingModule, FormsModule, HttpClientModule ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AppInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
AppModule은 Angular 애플리케이션의 기본 모듈입니다. 핵심 Angular 모듈을 가져오고 AppRoutingModule을 사용하여 라우팅을 설정합니다. 모듈은 다양한 제품 관련 구성요소를 선언합니다. 또한 AppInterceptor를 HTTP 인터셉터로 등록합니다. AppComponent는 부트스트랩 구성 요소로 설정되어 애플리케이션의 진입점이 됩니다.
import { Component } from '@angular/core' @Component({ selector: 'app-root', template: `<router-outlet></router-outlet>` }) export class AppComponent { }
app.comComponent.ts 파일은
import { Injectable } from '@angular/core'; import { HttpInterceptor } from '@angular/common/http'; import { HttpRequest, HttpErrorResponse } from '@angular/common/http' import { Observable, throwError } from 'rxjs' import { HttpHandler } from '@angular/common/http' import { HttpEvent } from '@angular/common/http' @Injectable({ providedIn: 'root' }) export class AppInterceptor implements HttpInterceptor { baseURL = 'http://localhost:8080/api' intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request.clone({ url: this.baseURL + request.url, })) } }
AppInterceptor 클래스는 서버로 전송되기 전에 모든 나가는 HTTP 요청 URL에 구성 가능한 baseURL을 추가하는 Angular HTTP 인터셉터입니다. 이를 통해 애플리케이션은 기본 API 엔드포인트를 중앙 집중화하고 쉽게 관리할 수 있습니다.
import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { ProductIndex } from './components/product/Index.component' import { ProductCreate } from './components/product/Create.component' import { ProductDetail } from './components/product/Detail.component' import { ProductEdit } from './components/product/Edit.component' import { ProductDelete } from './components/product/Delete.component' const routes: Routes = [ { path: '', redirectTo: 'product', pathMatch: 'full' }, { path: 'product', component: ProductIndex }, { path: 'product/create', component: ProductCreate }, { path: 'product/:id', component: ProductDetail }, { path: 'product/edit/:id', component: ProductEdit }, { path: 'product/delete/:id', component: ProductDelete } ] @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
AppRoutingModule은 제품 나열, 생성, 보기, 편집 및 삭제를 위한 제품 관련 경로를 포함하여 Angular 애플리케이션에 대한 라우팅을 설정합니다. 루트 경로 "/"에서 제품 목록 페이지 "/product"로 리디렉션되는 경로도 포함되어 있습니다.
import { Component } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { ProductService } from './Product.service' @Component({ selector: 'product-create', template: ` <div class="container"> <div class="row"> <div class="col"> <form ngNativeValidate method="post" (submit)="create()"> <div class="row"> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_name">Name</label> <input id="product_name" name="name" class="form-control" [(ngModel)]="product.Name" maxlength="50" /> <span *ngIf="errors.name" class="text-danger">{{errors.name}}</span> </div> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_price">Price</label> <input id="product_price" name="price" class="form-control" [(ngModel)]="product.Price" type="number" /> <span *ngIf="errors.price" class="text-danger">{{errors.price}}</span> </div> <div class="col-12"> <a class="btn btn-secondary" routerLink="/product">Cancel</a> <button class="btn btn-primary">Submit</button> </div> </div> </form> </div> </div> </div>` }) export class ProductCreate { product?: any = {} errors?: any = {} constructor(private router: Router, private route: ActivatedRoute, private ProductService: ProductService) { } create() { this.ProductService.create(this.product).subscribe(() => { this.router.navigateByUrl('/product') }, (e) => { alert(e.error) }) } }
ProductCreate 구성 요소는 이름과 가격에 대한 입력 필드를 제품 객체에 바인딩하여 새 제품을 생성하기 위한 양식을 제공합니다. 제출 시 ProductService를 호출하여 제품을 생성하고 제품 목록으로 다시 이동합니다. 해당 필드 옆에 유효성 검사 오류가 표시되며 생성 오류가 발생하면 경고가 발생합니다.
npm install -g @angular/cli@18.0.0 ng new view --minimal --routing --style css --no-standalone --ssr=false
Delete.comComponent.ts의 ProductDelete 구성 요소는 제품 삭제를 처리하는 Angular 구성 요소입니다. 제품 세부 정보(ID, 이름 및 가격)를 표시하는 읽기 전용 필드가 있는 양식을 표시합니다. 구성 요소가 초기화되면 경로의 제품 ID를 사용하여 제품 세부 정보를 가져옵니다. 양식 제출 시 delete() 메소드는 ProductService를 호출하여 제품을 삭제한 다음 제품 목록으로 리디렉션합니다. 삭제 중 오류가 발생하면 알림이 표시됩니다.
└─ src ├─ app │ ├─ app-routing.module.ts │ ├─ app.component.ts │ ├─ app.interceptor.ts │ ├─ app.module.ts │ └─ components │ └─ product │ ├─ Create.component.ts │ ├─ Delete.component.ts │ ├─ Detail.component.ts │ ├─ Edit.component.ts │ ├─ Index.component.ts │ └─ Product.service.ts ├─ index.html ├─ main.ts └─ styles.css
ProductDetail 구성요소는 특정 제품의 세부정보를 표시합니다. 경로의 ID를 기준으로 제품 정보를 검색하고 읽기 전용 필드에 제품 ID, 이름, 가격을 표시합니다. 구성요소는 탐색을 위한 "뒤로" 및 "편집" 버튼을 제공합니다. 구성 요소가 초기화되면 제품 세부 정보를 가져와 표시합니다.
import { enableProdMode } from '@angular/core' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { AppModule } from './app/app.module' platformBrowserDynamic().bootstrapModule(AppModule).catch(e => console.error(e))
ProductEdit 구성요소를 사용하면 사용자가 기존 제품을 편집할 수 있습니다. 경로에서 제품 ID를 사용하여 제품 세부 정보를 검색하고 이름 및 가격에 대해 편집 가능한 필드가 있는 양식에 표시합니다. 양식 제출 시 ProductService를 통해 제품을 업데이트하고 제품 목록으로 다시 이동합니다. 가져오거나 업데이트하는 동안 오류가 발생하면 경고로 표시되며, 유효성 검사 오류는 해당 필드 옆에 표시됩니다.
import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' import { AppInterceptor } from './app.interceptor' import { ProductIndex } from './components/product/Index.component' import { ProductCreate } from './components/product/Create.component' import { ProductDetail } from './components/product/Detail.component' import { ProductEdit } from './components/product/Edit.component' import { ProductDelete } from './components/product/Delete.component' @NgModule({ declarations: [ AppComponent, ProductIndex, ProductCreate, ProductDetail, ProductEdit, ProductDelete, ], imports: [ BrowserModule, AppRoutingModule, FormsModule, HttpClientModule ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AppInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
ProductIndex 구성 요소는 제품 목록을 표 형식으로 표시합니다. 초기화 시 ProductService에서 제품 목록을 가져오고 각 제품의 보기, 편집, 삭제를 위한 작업 버튼과 함께 각 제품의 ID, 이름, 가격을 표시합니다. 또한 제품 생성 페이지로 이동하는 버튼도 포함되어 있습니다.
import { Component } from '@angular/core' @Component({ selector: 'app-root', template: `<router-outlet></router-outlet>` }) export class AppComponent { }
ProductService는 Angular의 HttpClient를 사용하여 제품 관리를 위한 관련 HTTP 요청을 수행합니다. 다음과 같은 방법을 제공합니다.
import { Injectable } from '@angular/core'; import { HttpInterceptor } from '@angular/common/http'; import { HttpRequest, HttpErrorResponse } from '@angular/common/http' import { Observable, throwError } from 'rxjs' import { HttpHandler } from '@angular/common/http' import { HttpEvent } from '@angular/common/http' @Injectable({ providedIn: 'root' }) export class AppInterceptor implements HttpInterceptor { baseURL = 'http://localhost:8080/api' intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request.clone({ url: this.baseURL + request.url, })) } }
CSS는 컨테이너 위에 공간을 추가하고 버튼을 수평으로 간격을 두어 레이아웃을 조정합니다.
import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { ProductIndex } from './components/product/Index.component' import { ProductCreate } from './components/product/Create.component' import { ProductDetail } from './components/product/Detail.component' import { ProductEdit } from './components/product/Edit.component' import { ProductDelete } from './components/product/Delete.component' const routes: Routes = [ { path: '', redirectTo: 'product', pathMatch: 'full' }, { path: 'product', component: ProductIndex }, { path: 'product/create', component: ProductCreate }, { path: 'product/:id', component: ProductDetail }, { path: 'product/edit/:id', component: ProductEdit }, { path: 'product/delete/:id', component: ProductDelete } ] @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
HTML은 스타일 지정을 위한 Bootstrap, 아이콘을 위한 Font Awesome 및
npm install -g @angular/cli@18.0.0 ng new view --minimal --routing --style css --no-standalone --ssr=false
"example"이라는 테스트용 데이터베이스를 생성하고 Database.sql 파일을 실행하여 테이블과 데이터를 가져옵니다.
└─ src ├─ app │ ├─ app-routing.module.ts │ ├─ app.component.ts │ ├─ app.interceptor.ts │ ├─ app.module.ts │ └─ components │ └─ product │ ├─ Create.component.ts │ ├─ Delete.component.ts │ ├─ Detail.component.ts │ ├─ Edit.component.ts │ ├─ Index.component.ts │ └─ Product.service.ts ├─ index.html ├─ main.ts └─ styles.css
import { enableProdMode } from '@angular/core' import { platformBrowserDynamic } from '@angular/platform-browser-dynamic' import { AppModule } from './app/app.module' platformBrowserDynamic().bootstrapModule(AppModule).catch(e => console.error(e))
이 파일에는 데이터베이스 연결을 위한 구성 세부 정보가 들어 있습니다.
import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' import { FormsModule } from '@angular/forms' import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http' import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' import { AppInterceptor } from './app.interceptor' import { ProductIndex } from './components/product/Index.component' import { ProductCreate } from './components/product/Create.component' import { ProductDetail } from './components/product/Detail.component' import { ProductEdit } from './components/product/Edit.component' import { ProductDelete } from './components/product/Delete.component' @NgModule({ declarations: [ AppComponent, ProductIndex, ProductCreate, ProductDetail, ProductEdit, ProductDelete, ], imports: [ BrowserModule, AppRoutingModule, FormsModule, HttpClientModule ], providers: [ { provide: HTTP_INTERCEPTORS, useClass: AppInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
이 db.go 파일은 GORM과의 데이터베이스 연결을 구성합니다. SetupDatabase 함수는 환경 변수를 로드하고, MySQL 연결 문자열을 구성하고, 전역 DB 변수에 저장되는 GORM 인스턴스를 초기화합니다.
import { Component } from '@angular/core' @Component({ selector: 'app-root', template: `<router-outlet></router-outlet>` }) export class AppComponent { }
이 router.go 파일은 Gin 프레임워크를 사용하여 Go 애플리케이션에 대한 라우팅을 설정합니다. SetupRouter 기능은 CORS 미들웨어로 Gin 라우터를 초기화하여 모든 출처를 허용합니다. 이는 /api/products 경로 아래에 제품 관련 작업을 처리하기 위한 경로를 정의하며, 각각은 ProductController의 메서드에 매핑됩니다. 드디어 Gin 서버가 시작됩니다.
import { Injectable } from '@angular/core'; import { HttpInterceptor } from '@angular/common/http'; import { HttpRequest, HttpErrorResponse } from '@angular/common/http' import { Observable, throwError } from 'rxjs' import { HttpHandler } from '@angular/common/http' import { HttpEvent } from '@angular/common/http' @Injectable({ providedIn: 'root' }) export class AppInterceptor implements HttpInterceptor { baseURL = 'http://localhost:8080/api' intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request.clone({ url: this.baseURL + request.url, })) } }
이 product.go 파일은 GORM과 함께 사용할 제품 모델을 정의합니다. Id(자동 증가 기본 키), 이름, 가격의 세 가지 필드가 있는 제품 구조를 지정합니다.
import { NgModule } from '@angular/core' import { RouterModule, Routes } from '@angular/router' import { ProductIndex } from './components/product/Index.component' import { ProductCreate } from './components/product/Create.component' import { ProductDetail } from './components/product/Detail.component' import { ProductEdit } from './components/product/Edit.component' import { ProductDelete } from './components/product/Delete.component' const routes: Routes = [ { path: '', redirectTo: 'product', pathMatch: 'full' }, { path: 'product', component: ProductIndex }, { path: 'product/create', component: ProductCreate }, { path: 'product/:id', component: ProductDetail }, { path: 'product/edit/:id', component: ProductEdit }, { path: 'product/delete/:id', component: ProductDelete } ] @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule] }) export class AppRoutingModule { }
product_controller.go 파일은 Gin 프레임워크를 사용하여 Go 애플리케이션의 제품에 대한 CRUD 작업을 처리하는 메서드가 포함된 ProductController 구조체를 정의합니다.
import { Component } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { ProductService } from './Product.service' @Component({ selector: 'product-create', template: ` <div class="container"> <div class="row"> <div class="col"> <form ngNativeValidate method="post" (submit)="create()"> <div class="row"> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_name">Name</label> <input id="product_name" name="name" class="form-control" [(ngModel)]="product.Name" maxlength="50" /> <span *ngIf="errors.name" class="text-danger">{{errors.name}}</span> </div> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_price">Price</label> <input id="product_price" name="price" class="form-control" [(ngModel)]="product.Price" type="number" /> <span *ngIf="errors.price" class="text-danger">{{errors.price}}</span> </div> <div class="col-12"> <a class="btn btn-secondary" routerLink="/product">Cancel</a> <button class="btn btn-primary">Submit</button> </div> </div> </form> </div> </div> </div>` }) export class ProductCreate { product?: any = {} errors?: any = {} constructor(private router: Router, private route: ActivatedRoute, private ProductService: ProductService) { } create() { this.ProductService.create(this.product).subscribe(() => { this.router.navigateByUrl('/product') }, (e) => { alert(e.error) }) } }
이 main.go 파일은 Go 애플리케이션의 진입점입니다. 구성 및 라우팅 패키지를 가져온 다음 config.SetupDatabase()를 호출하여 데이터베이스 연결을 초기화하고 router.SetupRouter()를 호출하여 애플리케이션의 경로를 설정합니다.
Angular 프로젝트 실행
import { Component } from '@angular/core' import { ActivatedRoute, Router } from '@angular/router' import { ProductService } from './Product.service' @Component({ selector: 'product-delete', template: ` <div class="container"> <div class="row"> <div class="col"> <form ngNativeValidate method="post" (submit)="this.delete()"> <div class="row"> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_id">Id</label> <input readonly id="product_id" name="id" class="form-control" value="{{product.Id}}" type="number" required /> </div> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_name">Name</label> <input readonly id="product_name" name="name" class="form-control" value="{{product.Name}}" maxlength="50" /> </div> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_price">Price</label> <input readonly id="product_price" name="price" class="form-control" value="{{product.Price}}" type="number" /> </div> <div class="col-12"> <a class="btn btn-secondary" routerLink="/product">Cancel</a> <button class="btn btn-danger">Delete</button> </div> </div> </form> </div> </div> </div>` }) export class ProductDelete { product?: any = {} constructor(private router: Router, private route: ActivatedRoute, private ProductService: ProductService) { } ngOnInit() { this.get() } get() { return this.ProductService.delete(this.route.snapshot.params['id']).subscribe(data => { this.product = data }, e => { alert(e.error) }) } delete() { this.ProductService.delete(this.route.snapshot.params['id'], this.product).subscribe(() => { this.router.navigateByUrl('/product') }, (e) => { alert(e.error) }) } }
Go API 프로젝트 실행
import { Component } from '@angular/core' import { ActivatedRoute } from '@angular/router' import { ProductService } from './Product.service' @Component({ selector: 'product-detail', template: ` <div class="container"> <div class="row"> <div class="col"> <form ngNativeValidate method="post"> <div class="row"> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_id">Id</label> <input readonly id="product_id" name="id" class="form-control" value="{{product.Id}}" type="number" required /> </div> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_name">Name</label> <input readonly id="product_name" name="name" class="form-control" value="{{product.Name}}" maxlength="50" /> </div> <div class="mb-3 col-md-6 col-lg-4"> <label class="form-label" for="product_price">Price</label> <input readonly id="product_price" name="price" class="form-control" value="{{product.Price}}" type="number" /> </div> <div class="col-12"> <a class="btn btn-secondary" routerLink="/product">Back</a> <a class="btn btn-primary" routerLink="/product/edit/{{product.Id}}">Edit</a> </div> </div> </form> </div> </div> </div>` }) export class ProductDetail { product?: any = {} constructor(private route: ActivatedRoute, private ProductService: ProductService) { } ngOnInit() { this.get() } get() { return this.ProductService.get(this.route.snapshot.params['id']).subscribe(data => { this.product = data }, e => { alert(e.error) }) } }
웹 브라우저를 열고 http://localhost:4200
으로 이동합니다.
이 제품 목록 페이지를 찾을 수 있습니다.
'보기' 버튼을 클릭하시면 상품 상세페이지를 보실 수 있습니다.
제품을 수정하고 세부정보를 업데이트하려면 "수정" 버튼을 클릭하세요.
업데이트된 제품 세부정보를 저장하려면 "제출" 버튼을 클릭하세요.
새 제품을 추가하고 세부정보를 입력하려면 '생성' 버튼을 클릭하세요.
새 제품을 저장하려면 "제출" 버튼을 클릭하세요.
이전에 생성된 제품을 삭제하려면 "삭제" 버튼을 클릭하세요.
이 제품의 제거를 확인하려면 "삭제" 버튼을 클릭하세요.
결론적으로 Gin 프레임워크를 백엔드로 사용하여 API를 설정하는 동시에 구성 요소, 뷰 및 라우팅이 포함된 기본 Angular 프로젝트를 만드는 방법을 배웠습니다. 데이터베이스 작업에 GORM을 활용하여 강력하고 효율적인 백엔드와 완벽하게 통합되는 동적 프런트엔드를 성공적으로 구축했습니다. 이 조합은 최신 풀스택 웹 애플리케이션 개발을 위한 견고한 기반을 형성합니다.
소스 코드: https://github.com/stackpuz/Example-CRUD-Angular-18-Go
몇 분 만에 Angular CRUD 앱 만들기: https://stackpuz.com
위 내용은 Go API를 사용하여 Angular CRUD 앱 구축의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!