[nextjs]프로젝트요약/project summary(myapp30+)

👉🏻 이 프로젝트 요약은 myapp30부터 적용됩니다.
This project summary applies starting from myapp30.

👉🏻 전체 코드는 깃허브에서 확인 할 수 있습니다.
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

📁 프로젝트 시작(myapp)
Project Start (myapp)

npx create-next-app@latest

📁 SQLite설치 / Installing SQLite

cd ~/myapp project
npm install better-sqlite3
npm install -D @types/better-sqlite3

📁 chokidar 설치 / Installing chokidar

— 실시간 파일 및 폴더변경 감시기능
Real-time file and folder change monitoring function

npm install -D chokidar

📁 DDNS,https설정 / DDNS,https settings

📁 도메인허용, @userid 사용하기
Allowing domains, using @userid

— next.config.ts

import type { NextConfig } from "next";

const nextConfig: NextConfig = {
  /* allow domain*/
  allowedDevOrigins: ['aloy-horizon.duckdns.org', '*.duckdns.org'],

  /* host.domain.org/@userid */
  async rewrites() {
    return [
      {
        source: '/@:username',
        destination: '/usersui/:username',
      },
    ]
  }
};


export default nextConfig;

📁 이전 프로젝트에서 신규 프로젝트로 복사할 파일
Files to copy from the previous project to the new project

myapp project/  
├── app/  (Next.js App Router)
│   ├── .well-known/webfinger/route.ts  -> webfinger
│   ├── api/                  -> API
│   ├── users/                -> Inbox,Outbox,Post,Actor
│   ├── usersui/              -> Timeline UI,Theme
│   ├── layout.tsx            -> Privoder
├── lib/
├── data/                     -> Key
├── data.sqlite               -> Database(WAL)
├── data.sqlite-wal           -> Database(WAL)
├── data.sqlite-shm           -> Database(WAL)
├── Caddyfile                 -> https 
├── next.config.ts            -> DDNS,@
└── instrumentation.ts        -> Background Server

1)데이터베이스 파일은 3개 모두 옮깁니다.
Move all three database files.

2) 아래의 명령을 실행하면 파일을 data.sqlite하나로 병합하면 data.sqlite파일하나만 옮길 수 있습니다.
If you run the command below to merge the files into a single data.sqlite file, you only need to move that one file.

sqlite3 data.sqlite "PRAGMA wal_checkpoint(TRUNCATE);"

📁 디렉토리 구조 및 파일 상세 설명
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내 글 삭제했을 때 팔로워들에게 삭제 전파(myapp30)
Notify followers when my post is deleted (myapp30)

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

— 팔로우,언팔로우 / Follow,Unfollow

# 팔로우 (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(myapp30)
# myapp30 부터 GET방식도 지원합니다.
# 1) POST

curl -X DELETE https://aloy-horizon.duckdns.org/api/posts \
 -H "Content-Type: application/json" \
 -d '{"username":"user1","id":"e6c99652-572f-491a-9e61-05e67b0d58fc"}'

# 2) GET

curl -X DELETE "https://aloy-horizon.duckdns.org/api/posts?id=117204066261760455&username=user1"

— 좋아요 실행, 좋아요 취소 / Likes, Undo Likes

# 좋아요 실행 / 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, Cancle Boost


# 부스트 실행 / 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”

Leave a Reply