[데브인턴십 1기] AdminBro를 사용한 어드민 페이지 개발 후기
권지웅업데이트:
AdminBro 프레임워크 사용 후기
서버의 어드민 페이지 개발에 AdminBro라는 프레임워크를 사용하게 되었습니다. 어드민브로에 대한 소개와 개발과정을 공유하려고 합니다.
AdminBro 프레임워크
현재 헬로우봇 서버는 Nodejs + Express + TypeScript로 개발되어 있습니다. 따라서 만들게 될 어드민 페이지도 서버의 스펙과 맞추면 좋을 거 같다고 생각하여 관련된 프레임워크를 리서치해 보았습니다. Django의 경우 강력한 어드민 패널을 자체 제공하는데에 비해 Nodejs 쪽은 선택지가 별로 없더군요… 최종 후보로 react-admin, Adminbro 둘 중에서 고민을 했는데 자동적으로 제공되는 기능이 많고 기본 제공 UI가 상당히 괜찮은 Adminbro를 선택했습니다.
간략한 소개
NodeJs 서버의 Admin Panel을 리액트 컴포넌트로 자동 렌더링해주는 프레임워크입니다. (Official site: https://adminbro.com/)
-
지원중인 프레임워크 플러그인 : Express, Nest.js, Hapi, Koa.js => @adminbro/express 사용
-
지원중인 데이터베이스 : Mongoose, TypeOrm, Sequlize => @adminbro/typeorm 사용
- 장점: 깔끔한 UI, 자동으로 제공되는 기능이 많다, 커스터마이징 자유도가 높다
- 단점: 나온지 얼마 안되서 부족한 기능들이 있고, 버그도 적지만 존재.
동작 방식
위에 지원하는 데이터베이스에서 알 수 있듯이, 어드민 브로는 ORM/ODM 방식과의 연동을 필요로 합니다. typeorm 의 경우 아래와 같은 방식으로 연동하면 됩니다.
import { Database, Resource } from "@admin-bro/typeorm";
const AdminBro = require("admin-bro");
AdminBro.registerAdapter({ Database, Resource });
당연히 typeorm 커넥션이 만들어진 다음에 정의되어야 합니다. 다른 디비들은 공식 사이트에서 연결 방법을 확인할 수 있어요.
어드민 브로는 이렇게 연동된 정보를 바탕으로 REST API를 생성하고 이를 통해 디비에서 데이터를 호출하여 REACT component로 렌더링합니다.
어드민 브로에서 쓰는 용어들을 간략히 소개 드리겠습니다.
- Resource: 디비 테이블을 뜻합니다.
- Properties : 테이블에 속하는 멤버들을 나타냅니다.
- 각 프로퍼티들의 타입을 (string, number, …) 지정할 수 있습니다.
- 페이지 화면에서 해당 프로퍼티를 보여줄지 안 보여줄지 설정할 수 있습니다.
- components를 설정해주면 기존 리액트 컴포넌트 대신 직접 커스터마이징한 리액트 컴포넌트를 대신 렌더링 합니다.
- 액션 별로 다른 컴포넌트를 렌더링 할 수 있습니다.
- Actions : REST API의 호출을 담당합니다.
- 기본 제공으로 new, edit, list, show, delete, bulkDelete이 있고, 필요하면 기존 action 커스터마이징 및 새로운 action 추가 가능
- component를 설정해주면 기존 리액트 컴포넌트 대신 직접 커스터마이징한 리액트 컴포넌트를 대신 렌더링 합니다.
- before, after 함수로 request, response에 선처리 혹은 후처리가 가능합니다.
import { Database, Resource } from "@admin-bro/typeorm";
import { User } from "./models/entities/User";
const userOption = require("./admin/options/User.option");
const AdminBro = require("admin-bro");
AdminBro.registerAdapter({ Database, Resource });
const adminBro = new AdminBro({
resources: [{ resource: User, options: { navigation: sidebar.user, ...userOption } }],
rootPath: '/admin'
});
const router = AdminBroExpress.buildRouter(adminBro);
app.use(adminBro.options.rootPath, router)
리소스가 많아지고, 커스터마이징이 진행됨에 따라 옵션 코드가 너무 길어져서 저는 따로 옵션 모듈을 만들어 사용했습니다. 옵션 모듈은 해당 리소스에 대한 프로퍼티 정보와 액션 정보 등을 담고 있습니다.
module.exports = {
properties: {
mainImageUrl: {
components: {
list: AdminBro.bundle("../views/Package/MainUrl/list.tsx"),
show: AdminBro.bundle("../views/Package/MainUrl/show.tsx"),
edit: AdminBro.bundle("../views/Package/MainUrl/new.tsx"),
},
isVisible: { edit: true, list: true, show: true },
},
},
actions: {
list: {
before: async (request) => {
~~
return request;
},
}
}
}
디버깅 및 문제 해결
NoResourceAdapterError: There are no adapters supporting one of the resource
=> 알려진 에러, 어드민 브로가 테이블 정보를 불러오려면 TypeOrm Entity 모듈이 BaseEntity를 extends 해야 합니다.- 기록 생성시 Validation error on null value :
아래 이미지와 같이 payload가 제대로 전달이 됐음에도 불구하고 null 값이 전달된다는 에러가 발생했습니다.
=> user가 foregin key로 걸려있는데 user_seq 를 키로 넘겨주면 인식을 못함, request에서 user로 바꿔주면서 해결했습니다
- 참조 이미지
- Many to One relationship 으로 걸려있는 프로퍼티를 보여주기 위해서는 해당 프로퍼티의 typeorm 파일에 RelationId를 추가해주어야 합니다.
- @adminbro/upload 라는 모듈이 존재하지만, aws upload 시 public-read 설정이 안되는 버그 존재. 해당 모듈을 사용하지 않고 참고만 해서 직접 모듈 구현했습니다.
- 어드민 브로가 제공하는 로그인 모듈 사용시 bodyparser 플러그인과 충돌이 일어납니다.
- known bug : 어드민브로 플러그인을 마운트한 뒤에 body parser를 마운트 해야 합니다…
- 단순히 parser들 setup 코드를 뒤로 옮기면 테스트 통과를 못해서 해결 불가
- sol : app.use 사용시 미들웨어들은 스택으로 관리됩니다. (app._router.stack). 어드민브로 플러그인 부분을 bodyparser 보다 앞으로 순서를 바꿨습니다..
결과물
예시
마치며
typescript + React 로 개발해 본 것은 처음이라 좋은 경험이었습니다. 확실히 js보다 함수 및 변수 전달에 명확도가 높아져서 코드를 이해하고 개발하는데 생산성이 높아진 것 같습니다. 처음에 계획할 때는 option 정도만 필요할거라 생각했는데, 개발이 진행될수록 커스터마이징하는 모듈들이 많아지면서 코드 규모가 많이 커졌습니다. 특히 tsx 부분이 엄청 늘었네요. 어드민브로 사용시 커스터마이징 없이도 디비 테이블들 CRUD는 자동으로 제공해서 Node로 어드민 패널시 간편하게 이용하면 좋을 것 같습니다. (공수 대비 효율이 좋아요) 감사합니다!
댓글남기기