본문 바로가기
프로그래밍/nodejs

AdminJS란? / AdminJS 설치하기 (공식사이트 번역)

by 한코코 2022. 11. 15.

AdminJS란?

AdminJS는 Node.js 애플리케이션을 위한 오픈 소스 관리자 패널입니다. 응용 프로그램에 데이터베이스 스키마를 강제하지 않습니다. 대신 사용 중인 Node.js 서버 및 ORM/ODM과 통합됩니다. UI 구성 요소는 React로 작성되었으며 관리자 패널을 완전히 사용자 지정할 수 있습니다. AdminJS는 또한 관리자 패널 외부에서 사용하거나 다른 애플리케이션과 통합하는 데 사용할 수 있는 자체 REST API를 생성합니다.

 

지원되는 프레임워크가 많기 때문에 AdminJS 패널을 기존 Node.js 애플리케이션과 쉽게 통합할 수 있습니다.

  • Express.js (@adminjs/express)
  • Nest.js (@adminjs/nestjs)
  • Hapi.js (@adminjs/hapi)
  • Koa.js (@adminjs/koa)
  • Fastify.js (@adminjs/fastify)

AdminJS는 또한 자체 데이터베이스 스키마를 사용자에게 강요하지 않습니다. 대신 기존 데이터베이스와 연결하기 위해 여러 ORM 및 ODM을 지원합니다.

  • TypeORM (via @adminjs/typeorm)
  • Sequelize (via @adminjs/sequelize)
  • Mongoose (via @adminjs/mongoose)
  • Prisma (via @adminjs/prisma)
  • MikroORM (via @adminjs/mikroorm)
  • Objection (via @adminjs/objection

동시에 AdminJS 패널의 모양을 완전히 사용자 정의할 수 있습니다. 사용자 인터페이스는 React로 구축되며, 마찬가지로 새 React 사용자 정의 구성 요소를 작성하여 새 UI 요소를 추가하거나 기존 요소를 재정의할 수 있습니다.

 

 

 

 

 

 

 

 

 

AdminJS 시작하기

"시작하기" 섹션은 AdminJS 에코시스템의 개요 및 빠른 설명입니다.

 

개요

AdminJS 어플리케이션은 다음으로 구성됩니다.

  1. 핵심 패키지
  2. 플러그인(선택한 프레임워크용)
  3. (선택한 ORM/ODM용) 어댑터

 

패키지

먼저 핵심 패키지를 설치합니다.

$ yarn add adminjs

다음 플러그인 중 하나를 설치합니다.

$ yarn add @adminjs/express                # for Express server
$ yarn add @adminjs/nestjs                 # for Nest server
$ yarn add @adminjs/hapi                   # for Hapi server
$ yarn add @adminjs/koa                    # for Koa server
$ yarn add @adminjs/fastify                # for Fastify server

마지막으로 다음 adapter를 설치합니다.

$ yarn add @adminjs/typeorm                # for TypeORM
$ yarn add @adminjs/sequelize              # for Sequelize
$ yarn add @adminjs/mongoose               # for Mongoose
$ yarn add @adminjs/prisma                 # for Prisma
$ yarn add @adminjs/mikroorm               # for MikroORM
$ yarn add @adminjs/objection              # for Objection

 

설정

AdminJS 종속성을 설치한 후 다음을 진행하십시오.

프레임워크로 AdminJS를 설정하는 방법에 대한 지침은 플러그인 섹션입니다.

AdminJS 인스턴스를 데이터베이스에 연결하는 방법에 대한 지침은 어댑터 섹션을 참고하십시오.

 

프런트엔드 번들링

AdminJS는 자체 프런트엔드를 생성해야 합니다. 프로덕션 환경에서는 배포 프로세스의 빌드 단계에서 모든 프런트엔드 파일을 번들로 묶지만 개발 단계에서는 상당히 번거롭습니다.

이러한 이유로 모든 플러그인과 어댑터를 설정한 후에 adminJS.watch() 함수 호출을 추가해야 합니다. 이는 개발 환경(process.env.NODE_ENV === 'development')에만 영향을 미치며 백그라운드에서 별도의 번들링 프로세스를 시작합니다.

import AdminJS from 'adminjs'

const adminJS = new AdminJS({
    // ...
})

adminJS.watch()

 

 

 

 

 

 

 

 

 

 

 

플러그인 - Express

( 나는 express를 쓰고싶으니까 express만 번역을 넣었다. )

시작하기 전에 문서에 설명된 AdminJS 패키지를 설치했는지 확인하십시오

$ yarn add adminjs @adminjs/express

Express.js로 AdminJS 패널을 설정하려면 express피어 종속성을 설치하고 필요로 해야 합니다.

$ yarn add express tslib express-formidable express-session

그 후에 다음 중 한가지를 따르십시오.

 

자바스크립트

const AdminJS = require('adminjs')
const AdminJSExpress = require('@adminjs/express')
const express = require('express')

const PORT = 3000

const start = async () => {
  const app = express()

  const admin = new AdminJS({})

  const adminRouter = AdminJSExpress.buildRouter(admin)
  app.use(admin.options.rootPath, adminRouter)

  app.listen(PORT, () => {
    console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`)
  })
}

start()

이제 당신은 AdminJS 어플리케이션을 실행할 수 있습니다.

$ node app.js

 

 

타입스크립트

$ yarn add -D @types/express ts-node

import AdminJS from 'adminjs'
import AdminJSExpress from '@adminjs/express'
import express from 'express'

const PORT = 3000

const start = async () => {
  const app = express()

  const admin = new AdminJS({})

  const adminRouter = AdminJSExpress.buildRouter(admin)
  app.use(admin.options.rootPath, adminRouter)

  app.listen(PORT, () => {
    console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`)
  })
}

start()

이제 당신은 AdminJS 어플리케이션을 실행할 수 있습니다.

$ node app.js

 

 

 

인증을 추가하려면 AdminJSExpress.buildRouter대신, AdminJSExpress.buildAuthenticatedRouter를 반드시 사용해야 합니다. 또한 세션 정보를 유지하기 위해 세션 저장소를 설정해야 합니다. 아래 예에서는 Postgres 테이블에 connect-pg-simple세션을 저장하고 세션 저장소가 데이터베이스에 연결할 수 있도록 사용합니다.

 

 

 

자바스크립트

// 추가 종속성을 설치
$ yarn add connect-pg-simple

const AdminJS = require('adminjs')
const AdminJSExpress = require('@adminjs/express')
const express = require('express')
const Connect = require('connect-pg-simple')
const session = require('express-session')

const PORT = 3000

const DEFAULT_ADMIN = {
  email: 'admin@example.com',
  password: 'password',
}

const authenticate = async (email, password) => {
  if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
    return Promise.resolve(DEFAULT_ADMIN)
  }
  return null
}

const start = async () => {
  const app = express()

  const admin = new AdminJS({})

  const ConnectSession = Connect(session)
  const sessionStore = new ConnectSession({
    conObject: {
      connectionString: 'postgres://adminjs:adminjs@localhost:5435/adminjs',
      ssl: process.env.NODE_ENV === 'production',
    },
    tableName: 'session',
    createTableIfMissing: true,
  })

  const adminRouter = AdminJSExpress.buildAuthenticatedRouter(
    admin,
    {
      authenticate,
      cookieName: 'adminjs',
      cookiePassword: 'sessionsecret',
    },
    null,
    {
      store: sessionStore,
      resave: true,
      saveUninitialized: true,
      secret: 'sessionsecret',
      cookie: {
        httpOnly: process.env.NODE_ENV === 'production',
        secure: process.env.NODE_ENV === 'production',
      },
      name: 'adminjs',
    }
  )
  app.use(admin.options.rootPath, adminRouter)

  app.listen(PORT, () => {
    console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`)
  })
}

start()

이 authenticate함수는 양식에 제출한 자격 증명을 하드코딩된 DEFAULT_ADMIN 개체와 비교합니다. 귀하의 경우 authenticate 양식 자격 증명을 실제 데이터베이스 개체와 비교하기 위해 함수의 논리를 수정해야할 수 있습니다.

!! 이 예제를 복사하여 붙여넣으려면 데이터베이스 연결 문자열을 기능 문자열로 설정해야 합니다.

이제 AdminJS 패널 URL을 방문할 때 서버를 시작하고 로그인 양식을 볼 수 있어야 합니다.

$ node app.js

 

 

타입스크립트

// 추가 종속성을 설치
$ yarn add connect-pg-simple
$ yarn add -D @types/connect-pg-simple @types/express-session @types/express ts-node
import AdminJS from 'adminjs'
import AdminJSExpress from '@adminjs/express'
import express from 'express'
import Connect from 'connect-pg-simple'
import session from 'express-session'

const PORT = 3000

const DEFAULT_ADMIN = {
  email: 'admin@example.com',
  password: 'password',
}

const authenticate = async (email: string, password: string) => {
  if (email === DEFAULT_ADMIN.email && password === DEFAULT_ADMIN.password) {
    return Promise.resolve(DEFAULT_ADMIN)
  }
  return null
}

const start = async () => {
  const app = express()

  const admin = new AdminJS({})

  const ConnectSession = Connect(session)
  const sessionStore = new ConnectSession({
    conObject: {
      connectionString: 'postgres://adminjs:@localhost:5432/adminjs',
      ssl: process.env.NODE_ENV === 'production',
    },
    tableName: 'session',
    createTableIfMissing: true,
  })

  const adminRouter = AdminJSExpress.buildAuthenticatedRouter(
    admin,
    {
      authenticate,
      cookieName: 'adminjs',
      cookiePassword: 'sessionsecret',
    },
    null,
    {
      store: sessionStore,
      resave: true,
      saveUninitialized: true,
      secret: 'sessionsecret',
      cookie: {
        httpOnly: process.env.NODE_ENV === 'production',
        secure: process.env.NODE_ENV === 'production',
      },
      name: 'adminjs',
    }
  )
  app.use(admin.options.rootPath, adminRouter)

  app.listen(PORT, () => {
    console.log(`AdminJS started on http://localhost:${PORT}${admin.options.rootPath}`)
  })
}

start()

이 authenticate함수는 양식에 제출한 자격 증명을 하드코딩된 DEFAULT_ADMIN 개체와 비교합니다. 귀하의 경우 authenticate 양식 자격 증명을 실제 데이터베이스 개체와 비교하기 위해 함수의 논리를 수정해야할 수 있습니다.

!! 이 예제를 복사하여 붙여넣으려면 데이터베이스 연결 문자열을 기능 문자열로 설정해야 합니다.

이제 AdminJS 패널 URL을 방문할 때 서버를 시작하고 로그인 양식을 볼 수 있어야 합니다.

$ ts-node app.js

 

 

 

 

 

 

어댑터 - TypeORM

adminJS에서 지원되는 플러그인 중 하나를 사용하여 adminJS 인스턴스를 설정했는지 확인하십시오. @admin/typeorm 또한 이전 섹션에 설명된 대로 설치해야 합니다.

이 가이드는 해당 문서 또는 Nest.js를 사용하여 TypeORM을 설정했다고 가정합니다.

@adminjs/typeorm은 현재 아래 예와 같이 BaseEntity를 확장하는 entities만 자원합니다.

import { BaseEntity, Column, Entity, PrimaryGeneratedColumn } from 'typeorm'

@Entity({ name: 'organizations' })
export class Organization extends BaseEntity {
  @PrimaryGeneratedColumn()
  public id: number;

  @Column()
  public name: string;
}

 

entity(엔티티) 란?
- 사람, 장소, 물건, 사건, 개념 등과 같은 명사에 해당된다.
- 업무상 관리가 필요한 것에 해당된다.
- 저장되기 위한 어떤 것에 해당된다.
- 예) 엔티티 = 과목 / 수학, 영어, 국어 = 인스턴스 --> 엔티티에 포함되는 인스턴스

 

엔티티의 특징
1. 엔티티가 포함하는 인스턴스에 대해 유일한 식별자로 식별이 가능해야함
2. 엔티티는 지속적으로 존재하는 두개 이상의 인스턴스들의 조합이여야 함
3. 엔티티는 반드시 속성을 지녀야 함
4. 엔티티는 업무 프로세스에 의해 이용되어야 함
5. 엔티티는 다른 엔티티와 최소 한개 이상의 관계가 있어야 함

참고블로그1 / 참고블로그2

TypeORM을 Nest.js와 다른 플러그인에 연결하는 방법에는 약간의 차이가 있으므로 가이드는 이에 따라 두 섹션으로 나뉩니다.

 

기준

비 Nest.js 플러그인에 대한 구성은 기본적으로 각 플러그인에 대해 동일합니다.

  • 데이터 소스를 가져와서 초기화 해야 합니다.
  • AdminJSTypeorm 어댑터를 가져와서 등록해야합니다.
  • 사용하려는 entities를 가져와서 AdminJS resources options에 전달해 주어야 합니다.
// ... other imports
import * as AdminJSTypeorm from '@adminjs/typeorm'
import dataSource from './path/to/your/datasource'
import { Organization } from './organization.entity'

AdminJS.registerAdapter({
  Resource: AdminJSTypeorm.Resource,
  Database: AdminJSTypeorm.Database,
})

// ... other code
const start = async () => {
  // Make sure you initialize the data source before you create your AdminJS instance
  await dataSource.initialize()
  const adminOptions = {
    // We pass Organization to `resources`
    resources: [Organization],
  }
  // Please note that some plugins don't need you to create AdminJS instance manually,
  // instead you would just pass `adminOptions` into the plugin directly,
  // an example would be "@adminjs/hapi"
  const admin = new AdminJS(adminOptions)
  // ... other code
}

start()

 

Nest.js

Nest.js 설명서에 따라 app.module.ts를 설정하고 Nest.js 플러그인 튜토리얼도 따라야합니다.

app.module.ts는 다음을 포함하는 옵션을 가져와야(import)합니다.

  • TypeORM을 설정하기 위한 TypeOrmModule.forRoot(params)
  • AdminModule.createAdminAsync({ ... }

app.module.ts는 상단에 다음 import들을 추가힙니다.

// app.module.ts
import * as AdminJSTypeorm from '@adminjs/typeorm'
import AdminJS from 'adminjs'

import를 한 후 AdminJSTypeorm 어댑터를 어딘가에 등록합니다.

// app.module.ts
AdminJS.registerAdapter({
  Resource: AdminJSTypeorm.Resource,
  Database: AdminJSTypeorm.Database,
})

이렇게하면 AdminJS가 로드할 TypeORM 모델을 전달할 수 있습니다. 이전에 예로 사용했던 Organization entity를 사용하는 경우 app.module.ts로 가져와서 adminJsOptions의 리소스로 전달해야 합니다.

// app.module.ts
// ... other imports
import { Organization } from './organization.entity'
// ... other code
AdminModule.createAdminAsync({
  useFactory: () => ({
    adminJsOptions: {
      rootPath: '/admin',
      resources: [Organization],
    },
  }),
}),
// ... other code

 

 

 

 

 

 

 

 

어댑터 - 시퀄라이즈(Sequelize)

( adminJS는 기본적으로 Nest.js 설치한다고 가정을 했지만 나는 시퀄라이즈와 몽구스를 사용할 예정이라 두개만 번역했다. )

Sequelize를 Nest.js와 다른 플러그인에 연결하는 방법에는 약간의 차이가 있으므로 가이드는 이에 따라 두 섹션으로 나뉩니다.

// category.entity.ts
import { DataTypes, Model, Optional } from 'sequelize'
import sequelize from './index'

interface ICategory {
  id: number;
  name: string;
  createdAt: Date;
  updatedAt: Date;
}

export type CategoryCreationAttributes = Optional<ICategory, 'id'>

export class Category extends Model<ICategory, CategoryCreationAttributes> {
  declare id: number;
  declare name: string;
  declare createdAt: Date;
  declare updatedAt: Date;
}

Category.init(
  {
    id: {
      type: DataTypes.INTEGER,
      autoIncrement: true,
      primaryKey: true,
    },
    name: {
      type: new DataTypes.STRING(128),
      allowNull: false,
    },
    createdAt: {
      type: DataTypes.DATE,
    },
    updatedAt: {
      type: DataTypes.DATE,
    },
  },
  {
    sequelize,
    tableName: 'categories',
    modelName: 'category',
  }
)

 

기준

비 Nest.js 플러그인에 대한 구성은 기본적으로 각 플러그인에 대해 동일합니다.

  • AdminJSSequelize 어댑터를 가져와서 등록해야합니다.
  • 사용하려는 entities를 가져와서 AdminJS resources options에 전달해 주어야 합니다.
// app.ts
// ... other imports
import * as AdminJSSequelize from '@adminjs/sequelize'
import { Category } from './category.entity'

AdminJS.registerAdapter({
  Resource: AdminJSSequelize.Resource,
  Database: AdminJSSequelize.Database,
})

// ... other code
const start = async () => {
  const adminOptions = {
    // We pass Category to `resources`
    resources: [Category],
  }
  // Please note that some plugins don't need you to create AdminJS instance manually,
  // instead you would just pass `adminOptions` into the plugin directly,
  // an example would be "@adminjs/hapi"
  const admin = new AdminJS(adminOptions)
  // ... other code
}

start()

 

Nest.js

Nest.js 설명서에 따라 app.module.ts를 설정하고 Nest.js 플러그인 튜토리얼도 따라야합니다.

app.module.ts는 다음을 포함하는 옵션을 가져와야(import)합니다.

  • 시퀄라이즈를 설정하기 위한 SequelizeModule.forRoot({ uri: '...', dialect: '...' })
  • AdminModule.createAdminAsync({ ... }

Nest.js 데이터베이스 공급자를 사용하여 설정한 경우 Sequelize가 작동하도록 할 수도 있습니다. 요점은 AdminJS가 초기화되기 전에 데이터베이스 연결을 설정하는 것입니다.

app.module.ts는 상단에 다음 import들을 추가힙니다.

// app.module.ts
import * as AdminJSSequelize from '@adminjs/sequelize'
import AdminJS from 'adminjs'

import를 한 후 AdminJSSequelize 어댑터를 어딘가에 등록합니다.

// app.module.ts
AdminJS.registerAdapter({
  Resource: AdminJSSequelize.Resource,
  Database: AdminJSSequelize.Database,
})

이렇게하면 AdminJS가 로드할 Sequelize 모델을 전달할 수 있습니다. 이전에 예로 사용했던 Category entity를 사용하는 경우 app.module.ts로 가져와서 adminJsOptions의 리소스로 전달해야 합니다.

// app.module.ts
// ... other imports
import { Category } from './category.entity'
// ... other code
AdminModule.createAdminAsync({
  useFactory: () => ({
    adminJsOptions: {
      rootPath: '/admin',
      resources: [Category],
    },
  }),
}),
// ... other code

 

 

 

 

 

 

 

 

 

어댑터 - 몽구스(Mongoose)

Mongoose를 Nest.js와 다른 플러그인에 연결하는 방법에는 약간의 차이가 있으므로 가이드는 이에 따라 두 섹션으로 나뉩니다.

// category.entity.ts
import { model, Schema, Types } from 'mongoose'

export interface ICategory {
  title: string;
}

export const CategorySchema = new Schema<ICategory>(
  {
    title: { type: 'String', required: true },
  },
  { timestamps: true },
)

export const Category = model<ICategory>('Category', CategorySchema);

 

기준

비 Nest.js 플러그인에 대한 구성은 기본적으로 각 플러그인에 대해 동일합니다.

  • AdminJS 인스턴스를 생성하기 전에 Mongo를 연결해야 합니다.
  • AdminJSMongoose 어댑터를 가져와서 등록해야합니다.
  • 사용하려는 entities를 가져와서 AdminJS resources options에 전달해 주어야 합니다.
// app.ts
// ... other imports
import mongoose from 'mongoose'
import * as AdminJSMongoose from '@adminjs/mongoose'
import { Category } from './category.entity'

AdminJS.registerAdapter({
  Resource: AdminJSMongoose.Resource,
  Database: AdminJSMongoose.Database,
})

// ... other code
const start = async () => {
  await mongoose.connect('<mongo db url>')
  const adminOptions = {
    // We pass Category to `resources`
    resources: [Category],
  }
  // Please note that some plugins don't need you to create AdminJS instance manually,
  // instead you would just pass `adminOptions` into the plugin directly,
  // an example would be "@adminjs/hapi"
  const admin = new AdminJS(adminOptions)
  // ... other code
}

start()

 

Nest.js

Nest.js 설명서에 따라 app.module.ts를 설정하고 Nest.js 플러그인 튜토리얼도 따라야합니다.

app.module.ts는 다음을 포함하는 옵션을 가져와야(import)합니다.

  • 몽구스를 설정하기 위한 MongooseModule.forRoot('')
  • AdminModule.createAdminAsync({ ... }

app.module.ts는 상단에 다음 import들을 추가힙니다.

// app.module.ts
import * as AdminJSMongoose from '@adminjs/mongoose'
import AdminJS from 'adminjs'

import를 한 후 AdminJSMongoose 어댑터를 어딘가에 등록합니다.

// app.module.ts
AdminJS.registerAdapter({
  Resource: AdminJSMongoose.Resource,
  Database: AdminJSMongoose.Database,
})

이렇게하면 AdminJS가 로드할 Sequelize 모델을 전달할 수 있습니다. 이전에 예로 사용했던 Category entity를 사용하는 경우 app.module.ts로 가져와서 adminJsOptions의 리소스로 전달해야 합니다.

// app.modules.ts
// ... other imports
import { Category } from './category.entity'
// ... other code
AdminModule.createAdminAsync({
  useFactory: () => ({
    adminJsOptions: {
      rootPath: '/admin',
      resources: [Category],
    },
  }),
}),
// ... other code

댓글