Next.js로 만든 웹 앱 3개와 Flutter 앱, 그리고 이들이 보내는 메일입니다. 각각 쓰는 사람이 다릅니다.
독자용 사이트
apps/web-storefront
독자가 방문하는 사이트로, 출판사 자체 브랜드로 공개됩니다. 작품 둘러보기, 검색, 랭킹, 팔로우, 로그인, 그리고 페이지를 넘기며 읽는 뷰어를 갖추고 있습니다.
카탈로그테넌트가 가장 앞에 내세운 작품 아래로 이번 주 랭킹, 최신 에피소드, 요일별 연재, 테넌트가 추천하는 레이블과 작가가 이어집니다. 콘솔에서 고정한 공지는 독자가 닫을 때까지 모든 페이지 상단에 표시되며, 사이트 전체에 Publira가 아닌 출판사의 브랜드가 적용됩니다.
장르각 장르는 그 장르에서 순위가 높은 작품의 표지나 편집자가 올린 아트워크로 표시되고, 작품 수가 함께 나옵니다. 장르를 열면 이번 주 인기 작품 아래에 그 장르의 모든 작품이 나오며, 전체 목록과 같은 방식으로 정렬하고 필터링할 수 있습니다. 장르는 정해진 목록이 아니라 테넌트가 직접 정한 분류입니다.
랭킹일간 랭킹과 주간 랭킹이며, 각 행에 이전 집계 대비 순위 변동이 표시됩니다. 순위는 사람이 정하는 것이 아니라 에피소드 열람과 반응 같은 독자의 행동을 배치 작업이 집계해 정합니다. R15와 R18 작품은 별도 랭킹으로 집계되며, 열람할 수 있는 독자만 볼 수 있습니다.시리즈 목록공개된 모든 작품을 장르나 연재 상태로 정렬하고 필터링할 수 있으며, 무료 에피소드가 있는 작품만 볼 수도 있습니다. 연령 등급이 있는 작품은 어느 목록에서든 표시가 붙고, 독자가 열람 가능 여부를 확인하기 전까지 가려집니다.시리즈 상세표지, 줄거리, 레이블, 장르, 태그와 함께 모든 작가가 역할별로 표시되고, 그 옆에 팔로우와 공유 버튼이 있습니다. 아래에는 에피소드가 읽는 순서대로 나열되고 무료인지 유료인지 표시되므로, 로그인하기 전에도 지금 무엇을 읽을 수 있는지 알 수 있습니다.
에피소드 뷰어페이지는 canvas에 그려지며, 편집자가 지정한 펼침면 시작 위치부터 오른쪽에서 왼쪽으로 두 쪽씩 넘어갑니다. 도구 모음의 슬라이더는 펼침면 단위로 움직이고, 마지막 페이지를 넘기면 에피소드에 반응을 남기거나 다음 에피소드로 넘어갈 수 있습니다. 뷰어는 npm에 공개한 패키지 그대로이므로, 페이지 넘김과 펼침면, 읽는 방향은 라이브러리가 처리합니다.검색키워드 하나로 일치하는 작품, 작가, 레이블을 함께 찾고, 그룹마다 전체 목록을 열 수 있습니다. 테넌트가 공개한 것만 검색되므로, 아직 공개 대기 중인 작품은 여기서 찾을 수 없습니다.
작가테넌트에서 작품을 낸 작가와 작가별 작품 수를 보여 주는 목록, 그리고 사진, 프로필, 팔로우 버튼, 크레딧에 오른 모든 작품이 있는 작가 페이지입니다. 작가는 편집 콘솔에서 관리하는 프로필이므로, 같은 작가가 참여한 작품은 모두 한 페이지로 이어집니다.
로그인로그인과 회원 가입 양식 모두 이메일 주소와 비밀번호, 또는 독자가 이미 가진 Apple, Google 계정으로 이용할 수 있으며, 패스키를 등록한 독자는 패스키로도 로그인할 수 있습니다. 로그인 서비스는 테넌트가 직접 설정하며, 콘솔에 자격 증명을 등록하면 표시됩니다. 테넌트가 이용약관을 지정하면 독자에게 동의를 받고, 약관을 개정하면 다시 동의를 받습니다.
독자 개인 페이지팔로우한 작품과 작가의 새 소식, 구매한 에피소드와 각각의 열람 가능 기간, 그리고 새 에피소드 소식을 메일로 받을지 브라우저 알림으로 받을지 정하는 설정입니다.
테넌트 콘솔
apps/web-admin
편집자가 작업하는 콘솔입니다. 카탈로그와 공개 대기열, 결제와 로그인 서비스, 공개 사이트의 테마와 페이지, 독자 문의, 독자의 구매와 환불, 작가별 지급액을 다루며, 이 모든 변경 내역이 기록됩니다.
대시보드공개 중인 작품, 초안 에피소드, 공개 예정 건수 아래에 처리가 필요한 항목을 모은 대기열이 있습니다. 대기열에는 초안과 공개 예정 항목이 공개 일시와 함께 모이므로, 편집자는 콘솔을 열자마자 해야 할 일을 확인할 수 있습니다.
시리즈제목, 레이블, 공개일, 열람 기간, 연령 등급, 상태를 하나의 표로 보여 주고, 연재 상태나 연령 등급으로 필터링할 수 있습니다. 시리즈를 열면 상세 정보를 편집할 수 있습니다. 제목과 줄거리는 테넌트가 제공하는 언어별로 입력하고, 크레딧에는 작가마다 역할과 매출 배분율을 지정하며, 배분율을 입력하면 출판사 몫이 바로 계산됩니다. 이 크레딧은 새 에피소드를 만들 때 기본값으로 쓰입니다.
에피소드시리즈의 에피소드를 읽는 순서대로 나열하며, 번호를 일일이 고치지 않고 끌어서 놓거나 화살표 키로 순서를 바꿉니다. 에피소드 공개는 테넌트 시간대 기준 일시로 예약합니다. 페이지는 이미지, ZIP, EPUB으로 올리고 같은 방식으로 순서를 바꾸며, 공개한 뒤에도 페이지마다 교체하거나 삭제할 수 있습니다.
작가테넌트가 크레딧에 쓰는 작가 역할의 이름을 바꾸고 표시 순서를 정하며, 작가마다 상세 페이지가 있습니다. 테넌트 관리자가 이 페이지에서 작가 본인의 독자 계정을 연결하면, 작가는 자신의 에피소드를 구매하지 않고 읽을 수 있고 평가 요청도 받지 않습니다.
결제이 테넌트의 웹 결제에 쓰는 서비스(Stripe 또는 PAY.JP)와 그 키, 그리고 앱 판매에 쓰는 App Store와 Google Play 자격 증명을 설정합니다. 비밀 값은 저장만 할 수 있으며, 다시 열어도 설정 여부와 마스킹된 값만 보이고 실제 값은 표시되지 않습니다.
로그인과 앱 링크이 테넌트의 독자가 로그인에 쓸 수 있는 Apple과 Google 계정, 그리고 테넌트의 링크를 열 Android 앱과 iOS 앱을 설정합니다. 둘 다 테넌트별 설정이므로, 같은 설치 환경에 있는 출판사도 각자 자기 이름으로 독자를 로그인시키고 링크를 자기 앱으로 엽니다.
테마브랜드 색상과 그 위에 놓이는 글자 색, 독자에게 우선 적용할 서체를 공개 사이트의 로고, 아이콘과 함께 설정하고, 공개 사이트와 같은 컴포넌트로 만든 미리보기에서 확인합니다. 미리보기는 편집할 때마다 바로 바뀌지만 공개 사이트는 기존 색상을 유지하므로, 테마를 저장하기 전까지 독자에게는 아무것도 바뀌지 않습니다.
페이지와 공지테넌트의 페이지는 Markdown으로 작성하고 언어별 번역을 둘 수 있으며, 공개한 모든 버전이 남아 있어 비교하거나 되돌릴 수 있습니다. 공지는 사이트를 방문하는 모든 사람에게 표시되며, 그중 하나를 모든 페이지 상단에 배너로 고정할 수 있습니다.
문의독자가 사이트의 문의 양식으로 보낸 내용을 회신 주소, 담당 스태프, 처리 상태와 함께 모아 보여 줍니다. 문의 페이지에서 작성한 답변은 독자에게 메일로 발송되고 문의 아래에 기록됩니다. 새 문의가 오면 스태프에게도 메일이 가므로, 콘솔을 계속 열어 둘 필요가 없습니다.
리포트와 인세에피소드별로 끝까지 읽은 회원 수, 그리고 월별 매출, 환불, 작가별 지급액을 보여 줍니다. 월의 경계는 테넌트 시간대를 기준으로 합니다. 월을 마감하면 이후 매출이 반영되지 않는 명세서로 확정되어 CSV로 내려받을 수 있고, 마감 뒤에 기록된 환불은 다음 명세서에 조정으로 반영됩니다.감사 로그누가 언제 무엇을 했고 결과가 어땠는지를, 실행자와 작업 대상 기록과 함께 보여 줍니다. 기간, 작업, 실행자로 필터링할 수 있으며, 날짜는 서버가 아니라 테넌트 시간대의 날짜로 처리됩니다.
플랫폼 콘솔
apps/web-platform
테넌트보다 한 단계 위에서 설치 환경을 운영하는 사람을 위한 콘솔입니다. 설치 환경에 아직 필요한 설정, 테넌트 상태, 여러 테넌트에 걸친 이벤트, 모든 테넌트의 독자, 그리고 오브젝트 스토리지부터 보안 정책까지 모든 테넌트에 공통으로 적용되는 설정을 다룹니다.
대시보드설치 환경에 아직 필요한 설정과 각 설정 화면으로 가는 링크, 그 아래로 테넌트 수와 테넌트 생성, 역할 부여, 계정 추가 같은 여러 테넌트에 걸친 최근 이벤트가 표시됩니다. 운영자는 콘솔을 열면 설치 환경이 준비되었는지부터 확인할 수 있습니다.
테넌트등록된 모든 테넌트를 상태와 생성일과 함께 보여 주며, 이름이나 ID로 검색할 수 있습니다. 테넌트마다 프로필, 도메인, 멤버를 확인할 수 있습니다. 이 콘솔에서는 테넌트의 상태와 관리자만 관리하고, 카탈로그는 각 테넌트가 자체 콘솔에서 운영합니다.사용자모든 테넌트의 독자를 하나의 목록으로 모아 소속 테넌트, 가입일, 상태와 함께 보여 줍니다. 운영자는 여기서 계정을 정지하거나 삭제할 수 있지만, 그 독자가 어떤 작품을 읽을 수 있는지는 작품을 공개한 테넌트가 정합니다.
설치 환경 설정모든 테넌트의 업로드 파일이 저장되는 S3 호환 버킷, 설치 환경이 메일을 보낼 때 쓰는 SMTP 릴레이, 로그인과 메일 발송을 보호하는 제한, Web Push 서명에 쓰는 키 쌍을 설정합니다. 저장한 변경 사항은 재시작하지 않아도 실행 중인 모든 서버에 반영됩니다.
모바일 앱
mobile
같은 카탈로그를 휴대폰에서 볼 수 있습니다. Android와 iOS용 앱을 하나의 Flutter 코드베이스로 만들며, 테넌트의 이름과 테마로 표시됩니다. 사이트를 거치지 않고 공개 API를 직접 호출하고, 이메일 주소나 패스키, Apple, Google로 로그인할 수 있습니다. 에피소드는 스토어나 사이트 결제로 구매하며, 독자가 열었거나 저장한 에피소드는 기기에 보관됩니다.
카탈로그앱 바에는 테넌트의 이름과 테마가 적용되고, 콘솔에서 고정한 공지와 독자가 읽다 만 에피소드 아래로 장르와 이번 주 랭킹이 이어집니다. 뷰어를 제외한 모든 화면에서 하단 바를 통해 홈, 검색, 보관함, 알림, 계정으로 이동할 수 있습니다.
장르와 검색장르는 표지를 모은 타일로 표시되고, 장르를 열면 사이트와 같은 정렬과 필터로 작품을 볼 수 있습니다. 검색에서는 키워드 하나로 일치하는 작품, 작가, 레이블을 찾습니다. 모든 목록은 사이트와 같은 공개 API에서 가져오므로, 테넌트가 공개한 것만 찾을 수 있습니다.
작품과 작가표지, 테넌트가 정한 역할별 크레딧, 레이블, 장르, 태그와 함께 작품과 각 작가를 팔로우하는 버튼, 사이트 링크를 공유하는 버튼이 있습니다. 작가 페이지에서도 팔로우할 수 있으며, 팔로우한 작가의 새 에피소드가 나오면 푸시 알림이 오고, 알림을 누르면 바로 뷰어에서 열립니다.에피소드 뷰어사이트와 같은 에피소드를 편집자가 지정한 펼침면과 읽는 방향에 맞춰 표시하며, 슬라이더로 페이지를 이동할 수 있습니다. 유료 에피소드는 테넌트가 정한 방식에 따라 스토어의 결제 화면이나 사이트 결제로 구매합니다. 페이지를 캡처하면 페이지 이용에 관한 테넌트의 안내가 표시됩니다.
보관함과 알림보관함에는 이어서 읽기, 팔로우, 기기에 저장한 에피소드, 추천이 각각 목록으로 표시됩니다. 그 밖에 구매한 에피소드와 각각의 열람 가능 기간, 독자에게 온 알림도 볼 수 있습니다. 저장한 페이지는 기기에서 생성한 키로 암호화해 보관하며, 오프라인에서 읽은 기록은 네트워크에 다시 연결되면 전송됩니다.
계정아이콘, 이름, 이메일 주소, 비밀번호, 연결된 Apple과 Google 계정, 메일 알림, 테넌트의 페이지, 테넌트 문의, 계정 삭제까지 앱 안에서 모두 처리할 수 있습니다. 로그인 화면에서는 이메일 주소와 함께 Android에서는 Google, iOS에서는 Apple을 고를 수 있습니다.
발송되는 메일
apps/email-renderer
계정 관련 메일과 스태프가 받는 알림은 전용 렌더러가 React 컴포넌트로 HTML을 만들고, 일반 텍스트 파트와 함께 보냅니다. 테넌트의 독자와 스태프에게 가는 메일에는 Publira가 아니라 테넌트의 이름이 표시되며, 테넌트의 언어로 작성됩니다. 일본어, 한국어, 중국어에서 서로 다른 문자 사이의 간격은 사이트와 마찬가지로 메일 클라이언트의 텍스트 렌더링에 맡깁니다.
이메일 주소 확인새 독자가 처음 받는 메일입니다. 이메일 주소를 확인하는 버튼과 링크 만료 시각, 그리고 버튼이 표시되지 않는 메일 클라이언트를 위한 링크 주소가 들어 있습니다.비밀번호 재설정새 비밀번호를 설정하는 링크가 담긴 메일로, 링크는 발송 후 하루가 지나면 만료됩니다. 테넌트의 다른 메일과 같은 레이아웃을 사용합니다.스태프에게 가는 문의 알림독자가 문의 양식으로 보낸 내용과 함께 회신 주소와 독자가 로그인한 계정이 적혀 있어, 스태프는 콘솔의 문의 페이지에서도, 자신의 메일 클라이언트에서도 답할 수 있습니다.