👉🏻 이번에는 프로젝트를 추가하지 않고 지금까지 진행했던 과정을 한번 중간정리하는 시간을 가져보려고 합니다.
This time, instead of adding a new project, I’d like to take a moment to review and summarize the progress made so far.
👉🏻 프로젝트의 규모가 커지고 기능이 많아질 수록 프로젝트와 코드에 대한 이해가 무엇보다 중요합니다.
As a project grows in scale and complexity, understanding the project and the code becomes paramount.
👉🏻 지금까지의 과정에서 보듯이 하나의 기능이 추가되거나 수정될 때 관련된 다른 기능에 영향을 미치기 때문에 전체 코드를 살펴보고 수정해야 합니다.
As seen in the process so far, adding or modifying a single function affects other related functions, so the entire codebase must be reviewed and updated.
👉🏻 그래서 오늘은 프로젝트와 코드에 대해서 다시 한번 살펴보고 점검해보는 시간을 가지려고 합니다.
So, today I’d like to take some time to revisit and review the existing project and code.
👉🏻 전체 코드는 깃허브에서 확인 할 수 있습니다.
You can find the full code on GitHub.
https://github.com/gideonslife01/flm-nextjs
📁 전체 프로젝트 구조 / Overall Project Structure
myapp project/
├── app/ (Next.js App Router)
│ ├── .well-known/webfinger/route.ts -> webfinger
│ ├── api/follow/route.ts -> Follow API
│ ├── api/announce/route.ts -> Boost(Announcement)
│ ├── api/like/route.ts. -> Like API
│ ├── api/posts/route.ts -> Writing API
│ ├── api/timeline/route.ts -> Timeline API
│ ├── users/[username]/
│ │ ├── statuses/[id]/route.ts -> Indivisual Post
│ │ ├── route.ts -> Acotr Information
│ │ ├── followers/route.ts -> Followers List
│ │ ├── following/route.ts -> Following List
│ │ ├── inbox/route.ts -> Inbox
│ │ └── outbox/route.ts -> outbox
│ ├── usersui/[username]/
│ │ ├── page.tsx -> Timeline UI
│ │ └── _components/themes/
│ │ ├── themeex/ThemeexTheme.tsx -> Example Theme
│ │ ├── pinafore/PinaforeTheme.tsx -> Theme 1
│ │ ├── mastodon/MastodonTheme.tsx -> Theme 2
│ │ └── minimal/MinimaltTheme.tsx -> Theme 3
│ ├── layout.tsx, page.tsx, globals.css
│ └── favicon.ico
├── lib/
│ ├── theme.tsx -> Theme Provider
│ ├── watchThemes.ts -> Check real-time theme changes
│ ├── ap.ts -> Follow Accept
│ └── db.ts -> DB connection
├── data/
│ └── keys/ -> private.pem, public.pem
├── data.sqlite -> Database
├── Caddyfile -> https
├── instrumentation.ts -> Background Server
└── package.json
📁 디렉토리 구조 및 파일 상세 설명
Detailed Description of Directory Structure and Files
✔️ app/.well-known/webfinger/route.ts
– 다른서버에서 내 서버를 검색 / Search for my server on another server
- `acct:user1@aloy-horizon.duckdns.org` 검색하면 내 actor 주소 알려줌
Searching for `acct:user1@aloy-horizon.duckdns.org` returns my actor address.
- Mastodon에서 `@user1@aloy-horizon.duckdns.org` 검색할 때 사용함.
Use this when searching for `@user1@aloy-horizon.duckdns.org` on Mastodon.
- 없으면 다른 서버에서 내 서버를 못 찾음.
Without it, other servers won't be able to find your server.
- 테스트/Test: https://aloy-horizon.duckdns.org/.well-known/webfinger?resource=acct:user1@aloy-horizon.duckdns.org
✔️ app/api/follow/route.ts – 내가 다른 서버 팔로우 / I follow other servers.
- POST: 팔로우 시작 -> sendFollow()함수로 상대 inbox에 Follow 전송
Start following -> Send a follow request to the recipient's inbox using the sendFollow() function.
- DELETE: 언팔로우 -> sendUndoFollow()함수로 상대 inbox로 Undo Follow 전송
Unfollow -> Send an "Undo Follow" to the other party's inbox using the `sendUndoFollow()` function.
✔️ app/api/like/route.ts – 좋아요 / Likes
- POST: 좋아요/Like -> sendLike()
- DELETE: 좋아요 취소/Cancle Like -> sendUndoLike()
✔️app/api/posts/route.ts – 글 조회,쓰기,수정,삭제 / View, write, edit, and delete posts
- GET: 내 글만 조회/View only my posts (SELECT * FROM posts WHERE username = ?)
- POST: 글 쓰기 -> DB 저장 + followers에게 sendNote()로 배달!
Write a post -> Save to DB + Deliver to followers via sendNote()
- PUT: 글 수정 -> DB만 수정
Edit post -> Update DB only
- DELETE: 글 삭제 -> DB 삭제 + followers에게 sendDelete()로 삭제 전파
Delete post -> Delete from DB + propagate deletion to followers via sendDelete()
✔️ app/api/timeline/route.ts – posts(outbox) + inxbox_posts(inbox)
- GET: posts(내 글) + inbox_posts(남이 쓴 글) 최신순으로 최신순으로 검색
Search posts (my posts) + inbox_posts (posts by others) by recency.
- /usersui/user1 타임라인 UI가 이 API를 호출함
/usersui/user1 The timeline UI calls this API.
✔️ app/users/[username]/route.ts – Actor 프로필 / Actor Porfile
- GET: 내 프로필 정보 (publicKey, inbox, outbox, followers, following URL 알려줌)
My profile information (provides publicKey, inbox, outbox, followers, and following URLs)
- 상대 서버가 내 서버를 조회할 때 사용함.
Used when a remote server queries my server.
- 테스트/Test: https://aloy-horizon.duckdns.org/users/user1
- 없으면 연방 자체가 안 됨!
Without it, the federation itself cannot function!
✔️ app/users/[username]/statuses/[id]/route.ts – 개별 글 / Individual Posts
- GET: 글 1개 상세보기 / View post details
- 역할/Role:
1. posts 테이블에서 내 글 찾기
Find my posts in the 'posts' table
2. inbox_posts 테이블에서 남의 글 찾기
Finding posts by others in the inbox_posts table
3. Note JSON으로 반환 (to, cc, content 등)
Note: Returned as JSON (to, cc, content, etc.)
- 테스트/Test: /users/user1/statuses/e6c99652-572f-491a-9e61-05e67b0d58fc
- 없으면 Mastodon에서 내 글 클릭하면 404에러 발생함.
Without it, clicking on my post in Mastodon results in a 404 error.
✔️ app/users/[username]/following/route.ts
– 내가 팔로우 하는 사람 목록 / List of people I follow
GET: 내가 팔로우하는 사람들 목록
List of people I follow
✔️ app/users/[username]/inbox/route.ts
- POST: 상대 서버에서 오는 모든 Activity 받는 곳
Endpoint for receiving all activities from the remote server.
-- Follow 받음 -> following 테이블 추가 + sendAccept()로 수락
Upon receiving a follow request -> Add to the 'following' table + accept via `sendAccept()`.
-- Accept 받음 -> followers 테이블 추가!
Accept received -> Added to the 'followers' table!
-- Create(Note) 받음 -> inbox_posts 테이블에 저장(타임라인에 뜸)
Receive Create(Note) -> Save to `inbox_posts` table (appears on the timeline)
-- Like 받음 -> likes 테이블 저장
"Like" received -> Save to the 'likes' table
-- Announce(Boost) 받음 -> announces 테이블 저장
'Announce(Boost)' received -> Save to the 'announces' table!
-- Delete 받음 -> inbox_posts에서 삭제
Delete 'Received' -> Delete from inbox_posts
-- Undo 받음 -> 해당 데이터 삭제
Undo received -> Corresponding data deleted
- ActivityPub 연방 수신의 핵심 / The core of ActivityPub federal receiving
✔️ /users/[username]/outbox/route.ts – 내가 쓴 글 목록 / List of posts I’ve written
- GET: 내 글 목록을 ActivityPub 표준(OrderedCollection)으로 반환
Return the list of my posts in the ActivityPub standard (OrderedCollection) format.
- 상대 서버가 내 글을 가져갈 때 사용 함.
Used when a remote server retrieves my post.
- /api/posts랑 비슷하지만 outbox는 표준 포맷, /api/posts는 내 UI용
It's similar to /api/posts, but outbox is the standard format, while /api/posts is for my UI.
✔️ /usersui/[username]/page.tsx – 타임라인 UI / Timeline UI
- 브라우저에서 보는 타임라인 화면(테마를 파일을 불러옴)
Timeline view in the browser (loading the theme file)
- /api/timeline 호출해서 글 보여줌
Calls /api/timeline to display posts.
- themes/ - Pinafore, Mastodon, Minimal 테마 3개
themes/ - 3 themes: Pinafore, Mastodon, and Minimal
✔️ lib/ap.ts
- 중요함수 모음
Collection of Important Functions
- getPrivateKey(): username별 개인키 로드
getPrivateKey(): Load private key by username
- signAndSend(): 모든 Activity 서명 + 전송
signAndSend(): Sign and send all activities.
- sendFollow, sendAccept, sendNote, sendLike, sendAnnounce, sendDelete...
— lib/ap.ts 전체 함수 / full function
1)공용함수 / Common function
| 함수/Functions | 기능/features |
|---|---|
getPrivateKey(username) | username별 개인키 로드. data/keys/user1/private.pem 우선, 없으면 data/keys/private.pem fallback |
signAndSend(inbox, doc, username) | Activity JSON 서명(SHA-256 + RSA) + POST 전송. 모든 sendXXX가 이거 사용함. |
signedFetch(url, username) | GET 요청에 HTTP Signature 붙여서 가져오기. 상대방 actor 정보 조회할 때 사용함. |
2)Activity 전용 함수 / Activity-specific function
| 함수/Functions | 전송/Transmission | 사용/Use |
|---|---|---|
sendAccept(toInbox, followActivity, username) | Accept | 다른 사람이 날 팔로우했을 때, 팔로우 수락 When someone follows me, accept the follow |
sendNote(toInbox, note, username, postId, content) | Create + Note | 내가 글 썼을 때, 팔로워 inbox로 글 배달 When I write a post, it gets delivered to my followers’ inboxes. |
sendFollow(toInbox, targetActor, username) | Follow | 내가 다른 사람 팔로우할 때 When I follow someone else |
sendUndoFollow(targetActor, username) | Undo + Follow | 내가 언팔로우할 때 When I unfollow |
sendLike(toInbox, postId, username) | Like | 내가 다른 사람 글 좋아요할 때 When I ‘like’ someone else’s post |
sendUndoLike(toInbox, likeId, postId, username) | Undo + Like | 좋아요 취소할 때 When cancelling a ‘Like’ |
sendAnnounce(toInbox, postId, username) | Announce | 부스트/리포스트 할 때 When boosting/reposting |
sendUndoAnnounce(toInbox, announceId, postId, username) | Undo + Announce | 부스트 취소할 때 When canceling the boost |
sendDelete(toInbox, postId, username) | Delete + Tombstone | 내 글 삭제했을 때 팔로워들에게 삭제 전파(추가 예정) Notify followers when my post is deleted (feature coming soon) |
3) inbox 주소추출 / Extract email addresses from the inbox
| 함수 / function | 기능/features |
|---|---|
getActorData(actorUrl, username) | 상대방 https://mastodon.social/users/xxx 정보 가져와서 inbox 주소 추출Retrieve information for the user at https://mastodon.social/users/xxx and extract the inbox address. |
✔️lib/db.ts
- 데이터베이스 연결 ,테이블 및 인덱스 초기화
Database connection, table and index initialization
📁 컨텍스트 프로바이더 적용 부분
Implementation of the Context Provider
myapp project/
├── lib/
│ ├── theme.tsx -> Theme Provider (Context)
│ ├── watchThemes.ts -> Check real-time theme changes
│ ├── ap.ts -> Follow Accept
│ └── db.ts -> DB connection
└── app/
├── usersui/[username]/
│ ├── page.tsx -> Timeline UI (Use Provider)
│ └── _components/themes/
└── layout.tsx -> Apply the provider to the project
✔️/lib/theme.tsx – 테마 컨텍스트 프로바이더 / Theme context provider
- 테마 상태 전역 저장
Globally store the theme state. (useContext)
- currentTheme
/app/usersui/[username]/_components/themes/themeNames.tsx
- localStorage에 저장해서 새로고침해도 테마 유지
Save to localStorage to maintain the theme even after refreshing.
✔️ /lib/watchThemes.ts – 실시간 테마 감지 / Real-time theme detection
- 테마를 서버 재시작 없이 적용하기 위해서 사용함.
Used to apply the theme without restarting the server.
- /app/usersui/[username]/_components/themes/ 폴더 파일 변경 실시간 감지
Real-time detection of file changes in the /app/usersui/[username]/_components/themes/ folder
- chokidar로 파일 변경 감지
Detect file changes with chokidar
- instrumentation.ts에서 백그라운드로 실행
Run in the background in instrumentation.ts
- app/usersui/[username]/ 디렉토리내 index.ts 및 themeNames.ts파일 생성 및 업데이트
Create and update the index.ts and themeNames.ts files in the app/usersui/[username]/ directory.
- index.ts 및 themeNames.ts는 usersui/[username]/page.tsx에서 불러올 테마파일 경로가 기록됨.
`index.ts` and `themeNames.ts` contain the file paths for the themes to be imported in `usersui/[username]/page.tsx`.
✔️ /app/layout.tsx – 프로바이더 루트 / Provider root
— <ThemeProvider>로 전체 앱 감싸서 모든 곳에 theme변수 적용
Wrap the entire app with <ThemeProvider> to apply the theme variable everywhere.
import { ThemeProvider } from "@/lib/theme"; // ✅ myapp26
... ...
return (
<html
lang="en"
className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}
>
<body className="min-h-full flex flex-col">
<ThemeProvider>
{children}
</ThemeProvider>
</body>
</html>
);
✔️ /app/usersui/[username]/page.tsx – 테마호출 페이지 / Theme Call Page
- /api/timeline 호출해서 글도 가져오고, 테마도 적용
Call /api/timeline to fetch posts and apply the theme.
- 프로바이더에서 주입된 theme변수로 테마파일을 불러옵니다.
Load the theme file using the `theme` variable injected by the provider.
- 또는 pinafore디렉토리나 자동생성된 index.ts에서 테마파일을 불러옵니다.
Alternatively, import the theme file from the `pinafore` directory or the automatically generated `index.ts`.
- Code
const props = { timeline, username, onBoost: handleBoost, onLike: handleLike };
// getThemeComponent : import { themeRegistry, getThemeComponent } from './_components/themes'; --> index.ts
const ActiveTheme = getThemeComponent(theme as any) || themeRegistry.pinafore?.component || themeRegistry[themeNames[0]]?.component;
if (!ActiveTheme) return <div>No themes found</div>;
return <ActiveTheme {...props} />;
✔️ /app/usersui/[username]/_components/themes/ – 테마 / Theme
- 테마 파일이 있는 디렉토리
Directory containing theme files
1) themeex/ThemeexTheme.tsx -> Example Theme (예제/example)
2) pinafore/PinaforeTheme.tsx -> Theme 1 (Pinafore Style)
3) mastodon/MastodonTheme.tsx -> Theme 2 (Mastodon Style)
4) minimal/MinimaltTheme.tsx -> Theme 3 (Minimal Style)
✔️ /app/usersui/[username]/_components/themes/pinafore/PinaforeTheme.tsx
— 테마파일 중 하나입니다.
It is one of the theme files.
- select메뉴에서 테마를 선택해서 테마 변경
1) select 메뉴에서 메뉴를 선택
2) theme.tsx 파일에서 주입된 setTheme(newTheme); 함수실행
3) 로컬스토리지에 theme 이름을 저장하고
✔️ /instrumentation.ts – 백그라운드 서버 / Background Server
- Next.js 서버 시작 시 watchThemes.ts 실행
Execute watchThemes.ts when the Next.js server starts.
📁 전체 라우트 / Full Route
✔️ 브라우저에서 테스트 할 수 있는 라우트
Routes that can be tested in a browser
# 1. WebFinger
https://aloy-horizon.duckdns.org/.well-known/webfinger?resource=acct:user1@aloy-horizon.duckdns.org
# 2. Actor (프로필 / Profile)
https://aloy-horizon.duckdns.org/users/user1
# 3. Followers (남이 나 팔로우 / Someone follows me)
https://aloy-horizon.duckdns.org/users/user1/followers
# 4. Following (내가 남을 팔로우 / I follow others)
https://aloy-horizon.duckdns.org/users/user1/following
# 5. Outbox (내 글 목록 / My post list)
https://aloy-horizon.duckdns.org/users/user1/outbox
# 6. 타임라인 / Timeline
# - 타임라인UI / Timeline UI
https://aloy-horizon.duckdns.org/usersui/user1
# or
https://aloy-horizon.duckdns.org/@user1
# - 타임라인API / Timeline API
https://aloy-horizon.duckdns.org/api/timeline?username=user1
# 7. 개별 글(글 상세) / indivisual posts(Post Details)[posts, inbox_posts]
https://aloy-horizon.duckdns.org/users/user1/statuses/[ID]
# ex)
# posts table
https://aloy-horizon.duckdns.org/users/user1/statuses/e6c99652-572f-491a-9e61-05e67b0d58fc
# inbox_posts table
https://aloy-horizon.duckdns.org/users/user1/statuses/01M0P8VYEQTFW28N97W6MG8PY5
✔️ 터미널에서만 테스트 가능한 라우트
Routes that can only be tested in the terminal
# 팔로우 (following 1로 증가)
# Follow (following increased to 1)
curl -X POST https://aloy-horizon.duckdns.org/api/follow \
-H "Content-Type: application/json" \
-d '{"username":"user1","target":"https://freelifemakers.com/users/user1"}'
# 언팔로우 (following 0으로 감소)
# Unfollow (following count reduced to 0)
curl -X DELETE https://aloy-horizon.duckdns.org/api/follow \
-H "Content-Type: application/json" \
-d '{"username":"user1","target":"https://freelifemakers.com/users/user1"}'
# 글 쓰기 (outbox 7로 증가 + 팔로워들에게 배달)
# Writing a post (added to outbox 7 + delivered to followers)
curl -X POST https://aloy-horizon.duckdns.org/api/posts \
-H "Content-Type: application/json" \
-d '{"username":"user1","content":"Hello Fediverse! #test"}'
# 글 수정(로컬만가능, 구현예정, 라우트있음)
# Modify post (local-only, pending implementation, route exists)
curl -X PUT https://aloy-horizon.duckdns.org/api/posts \
-H "Content-Type: application/json" \
-d '{"username":"user1","id":"xxx","content":"수정된 내용/Revised content"}'
# ❗️연방 수정 추가하려면 위 sendUpdate 함수 추가,일단은 기능 보류,지원안되는 서버 많음.
# To add federal modifications, include the `sendUpdate` function mentioned above; however, this feature is currently on hold as many servers do not support it.
# - lib/ap.ts에 sendUpdate 함수추가
# Add the sendUpdate function to lib/ap.ts.
# - app/api/posts/route.ts의 PUT함수에 sendUpdate함수 추가
# Add the `sendUpdate` function to the `PUT` function in`app/api/posts/route.ts`.
# 글 삭제 / Delete Posts
curl -X DELETE https://aloy-horizon.duckdns.org/api/posts \
-H "Content-Type: application/json" \
-d '{"username":"user1","id":"e6c99652-572f-491a-9e61-05e67b0d58fc"}'
# 좋아요 실행 / Likes
curl -X POST https://aloy-horizon.duckdns.org/api/like \
-H "Content-Type: application/json" \
-d '{"username":"user1","target":"https://freelifemakers.com/users/user1/statuses/01M0XT1VBF23F8Y5EXAPK1VY56"}'
# 좋아요 취소하기 / Undo Likes
curl -X DELETE https://aloy-horizon.duckdns.org/api/like \
-H "Content-Type: application/json" \
-d '{"username":"user1","target":"https://mastodon.social/@Gargron/114559081070832514"}'
# 부스트 실행 / Boost
curl -X POST https://aloy-horizon.duckdns.org/api/announce \
-H "Content-Type: application/json" \
-d '{"username":"user1","target":"https://freelifemakers.com/@user1/statuses/01M10AHFHCKGF9BY5G4H50VSHG"}'
# 부스트 취소 / Cancle Boost
curl -X DELETE https://aloy-horizon.duckdns.org/api/announce \
-H "Content-Type: application/json" \
-d '{"username":"user1","target":"https://freelifemakers.com/@user1/statuses/01M10AHFHCKGF9BY5G4H50VSHG"}'
📁 관련 문서 / Related Documents
✔️ 1. 공식 스펙 (W3C)
Official Specification (W3C)
https://www.w3.org/TR/activitypub/
https://www.w3.org/TR/activitystreams-vocabulary/
✔️2.Mastodon 문서! (실질적 표준)
Mastodon Documentation! (De facto standard)
https://docs.joinmastodon.org/spec/activitypub/
✔️3. Fediverse 개발자 위키 – 호환성 정보
Fediverse Developer Wiki – Compatibility Information
https://codeberg.org/fediverse/fep
✔️ Mastodon 소스!(루비코드)
Mastodon source code! (Ruby code)
https://github.com/mastodon/mastodon/blob/main/app/lib/activitypub/activity/update.rb
요한복음 8장 32절 / John 8:32
“그리고 너희는 진리를 알게 될 것이며, 진리가 너희를 자유롭게 할 것이다.”
“Then you will know the truth ,and the truth will set you free”