**「GORM(Postgres)+ React / Next.js フロント分離構成」**ですね。
この組み合わせだと、記事保存失敗の原因はかなりパターンが絞れます。
しかもこの構成、実務でも超メジャーなので「あるある原因」がハッキリしています。
夜勤明けとのことなので、後で見返せる“実戦チェック表”形式でまとめますね。
🌟 この構成で一番多い失敗ポイント
この4層のどこかで止まっています。
🥇 最頻出:JSONがGo構造体に入っていない
React/Next.js構成だとほぼこれがトップです。
❌ よくある原因
フロント送信
Go構造体
👉 これだと タグが無いとDecode失敗する場合あり
✔ 修正(超重要)
🥈 user_idが保存できていない(認証連携)
React分離構成だとここ激増します。
よくある流れ
✔ 確認
または
が nilになっていないか
🥉 GORMエラーを見ていない
GORMはエラーを握りつぶしやすいです。
❌
✔ 必須
🏅 React側でCookie送信していない
Next.js分離構成では超重要です。
❌
👉 Cookieが送られない
👉 ログイン扱いにならない
✔ 修正
⭐ Next.js構成の超盲点
CORS設定
Go側
これが無いと
👉 Cookie送れません
⭐ GORM + Postgres特有のミス
UUID未設定
あなたはUUID使うこと多いのでここ注意です。
❌
ID生成していない
✔
⭐ GORM保存時の安全テンプレ
実務でよく使われる形です。
⭐ この構成の「原因ランキング」
GORM+React+Postgres限定
🥇 ReactがCookie送ってない
🥈 JSONタグ不足
🥉 user_id未取得
④ UUID未生成
⑤ CORS設定不足
🌸 実務デバッグ最短コース
夜勤明けでも追える順番です。
Step1
Go側ログ
Step2
記事POST時のリクエスト確認
(ブラウザ開発者ツール)
👉 Cookie付いているか
Step3
DB直接確認
👍 もしよければ(かなり絞れます)
落ち着いた時でいいので教えてください。
① 認証方式
-
JWT
-
Cookieセッション
-
Redisセッション
② Next.jsは
-
App Router?
-
Pages Router?
③ 記事保存APIは
-
REST?
-
GraphQL?
🌙 夜勤明けエンジニアのための「GORM+React分離構成」深掘り解説
お疲れさまです! これから寝るかもですが、後で読み返せるように雑学・業界あるある込みで解説しますね。
🎯 認証方式の選択:JWT vs Cookie vs Redis
🥇 JWT(JSON Web Token)
業界での立ち位置
- 2015年頃から爆発的に普及
- 「マイクロサービス時代の認証標準」みたいな扱い
- でも実は落とし穴だらけで、今は揺り戻しが起きてる
実装の現実
// よくある実装
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"user_id": userID,
"exp": time.Now().Add(time.Hour * 24).Unix(),
})
React側(ここが超重要)
// ❌ 初心者がやりがちな間違い
localStorage.setItem('token', token)
// 👆 XSS攻撃で盗まれる危険性大
```
**業界あるある**
- 「JWTは安全!」と思って導入
- → XSS脆弱性でトークン盗まれる
- → 慌ててhttpOnlyクッキーに戻す
- → 「最初からCookie使えばよかった...」
**JWTの落とし穴(実務で超重要)**
```
問題1: ログアウトできない
→ トークンに有効期限あるので、サーバー側で無効化できない
→ 結局Redisでブラックリスト管理(本末転倒)
問題2: payload肥大化
→ 権限情報をJWTに詰め込みすぎ
→ 毎回数百バイトのトークン送信
→ Cookie+セッションIDの方が軽い
🥈 Cookieセッション
業界での再評価
- 2020年頃から「実は堅実」と見直され中
- GitHub、Stack Overflowなど大手も普通に使ってる
GORM+Postgres構成での実装
// Gorilla sessions(定番ライブラリ)
store := sessions.NewCookieStore([]byte("secret-key"))
// セッション保存
session.Values["user_id"] = userID
session.Save(r, w)
React側の超重要ポイント
// ✅ これがないとCookie送信されない
fetch('/api/articles', {
method: 'POST',
credentials: 'include', // ← これ!
headers: {
'Content-Type': 'application/json'
}
})
業界あるある
- Reactエンジニア「なんか認証通らない...」
- →
credentials: 'include'忘れてる(9割これ) - Go側のCORS設定も要チェック:
// ❌ これだけだとダメ
w.Header().Set("Access-Control-Allow-Origin", "*")
// ✅ Cookie使うならこう
w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000")
w.Header().Set("Access-Control-Allow-Credentials", "true")
// ↑ これがないとブラウザがCookie送らない
```
**Cookieの落とし穴**
```
問題1: SameSite属性
→ Chrome 80以降、デフォルトで Lax
→ フロント・バックエンドのドメインが違うと送信されない
→ SameSite=None; Secure が必要
問題2: ローカル開発
→ localhost:3000 → localhost:8080 は「別ドメイン」扱い
→ 開発時は SameSite=Lax で動く
→ 本番(別ドメイン)でいきなり動かない
🥉 Redisセッション
業界での使われ方
- スケールする前提のサービスで採用
- でも「最初からRedis入れる必要ある?」論争あり
GORM+Postgres+Redis構成
import "github.com/go-redis/redis/v8"
// セッション保存
sessionID := uuid.New().String()
rdb.Set(ctx, sessionID, userID, 24*time.Hour)
// Cookie送信
http.SetCookie(w, &http.Cookie{
Name: "session_id",
Value: sessionID,
})
```
**業界の本音**
- 「将来スケールするから」← 9割スケールしない
- でも採用面接で「Redis使ってます」は印象良い(悲しい現実)
**Redisの実務あるある**
```
シナリオ: 記事保存時にセッション取得失敗
原因1: Redisのメモリ上限
→ セッション削除されてる
→ ユーザーは「ログインしてるのに...」
原因2: Redis接続プールの枯渇
→ GORM(DB接続)は成功
→ Redis接続失敗
→ user_id取得できず保存失敗
📱 Next.js:App Router vs Pages Router
🔥 App Router(2023年〜安定版)
業界での評価
- 「革命的!」vs「複雑すぎ...」で真っ二つ
- 大企業は様子見、スタートアップは早期導入多い
GORM連携の注意点
// app/articles/page.tsx
async function ArticlePage() {
// ❌ これはサーバーコンポーネント
// fetch時に Cookie 自動送信される
const res = await fetch('http://localhost:8080/api/articles')
// でもクライアント側で POST したい時は...
}
'use client' // ← これ付けるとクライアントコンポーネント
async function handleSubmit() {
// ✅ こっちは credentials: 'include' 必須
await fetch('/api/articles', {
credentials: 'include'
})
}
業界あるある
- 「サーバーコンポーネントとクライアントコンポーネントの違いがわからん」
- → みんな最初混乱する(公式ドキュメントも説明が...)
- → 結局
'use client'を全部に付けちゃう(本末転倒)
🛠️ Pages Router(従来型)
業界での立ち位置
- 「枯れた技術」= 安定してる
- Next.js 14でもサポート継続中
GORM連携がシンプル
// pages/articles/new.tsx
export default function NewArticle() {
const handleSubmit = async (data) => {
await fetch('/api/articles', {
method: 'POST',
credentials: 'include',
body: JSON.stringify(data)
})
}
}
実務の本音
- App Routerの学習コスト vs Pages Routerの安定性
- → 「新規プロジェクトでも Pages Router 選ぶ」チーム多い
- → でも採用面接では「App Router知ってます」言った方が印象良い(またか)
🌐 API設計:REST vs GraphQL
🥇 REST API(王道)
GORM+Postgres での実装
// 記事保存エンドポイント
r.HandleFunc("/api/articles", CreateArticle).Methods("POST")
func CreateArticle(w http.ResponseWriter, r *http.Request) {
var article Article
// ❌ よくある失敗
json.NewDecoder(r.Body).Decode(&article)
// ✅ エラーチェック必須
if err := json.NewDecoder(r.Body).Decode(&article); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// user_id 取得(ここ超重要)
session, _ := store.Get(r, "session")
userID := session.Values["user_id"].(string)
article.UserID = userID
// GORM保存
result := db.Create(&article)
if result.Error != nil {
log.Println("保存失敗:", result.Error) // ← これがないと原因不明
http.Error(w, "保存失敗", 500)
return
}
}
業界あるある:JSONタグ忘れ
// ❌ これだと React からのJSONをパースできない
type Article struct {
Title string
Content string
}
// ✅ 必須
type Article struct {
Title string `json:"title"`
Content string `json:"content"`
UserID string `json:"-" gorm:"column:user_id"` // JSONには含めない
}
```
**実務の落とし穴**
```
React送信: { "title": "記事", "content": "本文" }
↓
Go構造体: Title, Content (大文字)
↓
JSONタグなし
↓
パース失敗(でもエラーログ出ない)
↓
空データがDBに保存される
↓
「あれ?記事の中身が空...」
🥈 GraphQL
業界での位置づけ
- 2018-2020年:「REST終わった!」ブーム
- 2021年〜:「やっぱREST でいいかも...」揺り戻し
GORM連携(gqlgen使用)
// schema.graphql
type Mutation {
createArticle(input: ArticleInput!): Article!
}
// resolver.go
func (r *mutationResolver) CreateArticle(ctx context.Context, input ArticleInput) (*Article, error) {
// user_id 取得(GraphQLでもここは同じ)
userID := ctx.Value("user_id").(string)
article := &Article{
Title: input.Title,
Content: input.Content,
UserID: userID,
}
result := r.DB.Create(article)
return article, result.Error
}
React側(Apollo Client)
const [createArticle] = useMutation(CREATE_ARTICLE, {
context: {
credentials: 'include' // ← GraphQLでも必要
}
})
```
**GraphQLの現実**
```
メリット:
- 必要なデータだけ取得できる
- フロントエンドが柔軟
デメリット:
- 学習コスト高い
- N+1問題(GORMと相性悪い)
- キャッシュ戦略が複雑
- 「結局REST で済んだ」ってなる
```
**業界の本音**
- スタートアップ:「GraphQL使ってみたけど複雑で...」
- 大企業:「既存のREST APIで十分」
- Netflix、GitHub:GraphQL使ってる(でも彼らは超大規模)
---
## 🔍 実務デバッグ:構成別チェックリスト
### パターン1:JWT + App Router + REST
```
✅ チェック項目
□ JWTをlocalStorageに保存してない?
□ Authorization ヘッダー付けてる?
□ Go側でJWT検証してる?
□ トークン期限切れ処理してる?
```
### パターン2:Cookie + Pages Router + REST(最頻出)
```
✅ チェック項目
□ credentials: 'include' 付けた?
□ CORS設定で Allow-Credentials: true?
□ SameSite属性 確認した?
□ Go側でセッション取得エラーチェックしてる?
□ user_id が nil になってない?
```
### パターン3:Redis + App Router + GraphQL
```
✅ チェック項目
□ Redis接続プール設定してる?
□ セッションID取得失敗時のエラーハンドリングは?
□ N+1問題対策してる?(Dataloaderとか)
□ GraphQL context に認証情報渡してる?
```
---
## 💡 夜勤明けエンジニアへの処方箋
**疲れてる時の最短デバッグ**
```
Step 1: ブラウザ開発者ツール開く
→ Network タブ
→ 記事POST時のリクエスト見る
→ Cookie 付いてる?
Step 2: Go側ログ追加
log.Println("リクエストBody:", r.Body)
log.Println("セッション:", session.Values)
log.Println("GORM Error:", result.Error)
Step 3: Postgres直接確認
SELECT * FROM articles ORDER BY created_at DESC LIMIT 5;
→ データ入ってる?
→ user_id NULL になってない?
業界の先輩からのアドバイス
- 「認証まわりは9割Cookieの問題」
- 「GORMのエラーは必ずログ出せ」
- 「フロント・バック分離は CORS が鬼門」
寝る前に読めなくても大丈夫です、このチェックリストはずっと使えます!
何か特定の部分で詰まってたら、起きた後でいいので教えてください 🌙
GORM + React/Next.js分離構成の実戦チェックリスト(業界話付き)
なるほど、この構成ですね。最近のスタートアップから中堅SIerまで「Goバックエンド + React/Next.jsフロント」はまさに「令和のLAMPスタック」と言えるほど普及しています。それぞれの判断理由が面白いんです。
🍵 業界の「なぜこの構成?」雑学
Go側の事情:
-
SIer系:「Java/PHPから脱却したいが、TypeScript全盛でGoに流れる中堅エンジニア」
-
スタートアップ:「初期メンバーが元GoogleでGo推し」「AWS LambdaでGoの起動速さが重要」
-
共通理由:「コンパイル言語の安心感+GORMの手軽さ」というバランス
React/Next.js側の事情:
-
2023年以降のデファクト:App Router登場で「Next.js一択」化が加速
-
面白い事実:Pages Router時代は「SPA vs SSR」論争が多かったが、App Routerで収束
-
SIerあるある:「とりあえずNext.js」案件が急増、現場ではApp Router理解が追いつかない…
🔍 あなたの質問への詳細解説
① 認証方式の実態と「あるある」
JWT(JSON Web Token)
// 最近のトレンド:httponly cookieに入れる派閥が増加 http.SetCookie(w, &http.Cookie{ Name: "token", Value: tokenString, HttpOnly: true, // ← これが重要 Secure: true, })
-
メリット:ステートレス、マイクロサービス連携が楽
-
デメリット:「ログアウト問題」が有名(トークンの無効化が難しい)
-
業界あるある:JWTをlocalStorageに入れてXSS被害 → 「やっぱりhttponly cookieに戻す」案件多数
Cookieセッション
// セッションIDを発行 session, _ := store.Get(r, "session-name") session.Values["user_id"] = userID session.Save(r, w)
-
メリット:シンプル、サーバー側で制御可能
-
デメリット:スケーリング時にRedisが必要
-
SIerあるある:「Redis嫌い」の現場はファイルセッションで頑張る → 性能問題で結局Redis導入
Redisセッション
store, _ := redis.NewStore(10, "tcp", "localhost:6379", "", []byte("secret"))
-
2024年現在のデファクト:KVSとしてRedisが圧倒的
-
面白い変化:以前は「Memcached vs Redis」論争があったが、Redisの機能豊富さで決着
-
クラウド事情:AWS ElastiCache(Redis)、GCP Memorystoreが主流
② Next.jsルーティングの深層
App Router(新しい方)
app/
api/
articles/
route.ts ← API Routes
page.tsx ← ページコンポーネント
-
Vercelの野望:「Reactのメタフレームワーク」としての地位確立
-
学習コスト:Server Components、Suspenseなど概念が増加
-
現場の声:「便利だが、チーム全員が理解するまでが大変」
-
超重要注意点:
fetch()のキャッシュ挙動が複雑化
Pages Router(従来)
pages/
api/
articles.ts ← API Routes
index.tsx ← ページ
-
2024年現在:既存プロジェクトの大半はまだこちら
-
移行あるある:「App Routerに移行しよう会議」→ 工数見積もりで延期決定
-
SIerの現実:受託開発では「Pages Routerで安定」を選択する現場が多い
③ API設計のトレンド
REST(まだまだ現役)
// GORMとの相性が抜群 type Article struct { gorm.Model Title string `json:"title"` Content string `json:"content"` UserID uint `json:"user_id"` }
-
GORMあるある:
gorm.Model便利だが、UUID使う時は要注意 -
現場の妥協:「RESTっぽいAPI」が多数(厳密なRESTfulでない)
GraphQL(ニッチだが強い)
// gqlgenが人気 type Article struct { ID string `json:"id"` Title string `json:"title"` Content string `json:"content"` }
-
採用パターン:
-
モバイルアプリとWebで同じAPIを使いたい
-
複雑なデータ取得(SNSのタイムラインなど)
-
「最先端感」を出したいスタートアップ
-
-
現実:キャッシュ・バッチ処理が難しく、途中でRESTに戻るケースも
🚨 この構成の「死亡フラグ」シグナル
夜勤明けでも見逃しがちな超危険信号:
-
「Next.jsのfetchが開発と本番で挙動違う」
-
原因:App Routerのキャッシュ挙動
-
対策:
fetch('/api', { cache: 'no-store' })
-
-
「ローカルでは動くけど本番で動かない」
-
原因①:CORS設定(特にcredentials)
-
原因②:CookieのSameSite属性
-
-
「たまにユーザー情報が消える」
-
原因:JWTの有効期限 vs セッションタイムアウトの不一致
-
🛠 実務で使える「生存テンプレート」
Go側(最小限安全版):
// middleware/cors.go func CORSMiddleware() gin.HandlerFunc { return func(c *gin.Context) { c.Writer.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") c.Writer.Header().Set("Access-Control-Allow-Credentials", "true") // ← 必須! c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type") c.Next() } } // handlers/article.go func CreateArticle(c *gin.Context) { var article Article // JSONデコード(エラーチェック必須) if err := c.ShouldBindJSON(&article); err != nil { c.JSON(400, gin.H{"error": err.Error()}) return } // 認証情報取得(方式による) userID, exists := c.Get("user_id") if !exists { c.JSON(401, gin.H{"error": "unauthorized"}) return } article.UserID = userID.(uint) // UUID生成(使う場合) article.ID = uuid.New() // DB保存(エラーチェック必須) if result := db.Create(&article); result.Error != nil { log.Printf("DB error: %v", result.Error) // ← ログ必須 c.JSON(500, gin.H{"error": "database error"}) return } c.JSON(201, article) }
Next.js側(App Router):
// app/articles/page.tsx export default function ArticlePage() { const createArticle = async (data: ArticleData) => { const res = await fetch('http://localhost:8080/api/articles', { method: 'POST', credentials: 'include', // ← Cookie送信 headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(data), }); if (!res.ok) { // エラー詳細をログに残す const error = await res.text(); console.error('API error:', error); throw new Error('Failed to create article'); } return res.json(); }; return ( // JSX ); }
🌸 夜勤明けデバッグ・マインド
「Go + React分離構成」のバグは大体:
-
通信問題(CORS、Cookie、HTTPS)
-
型不一致(JSONタグ、NULL許容)
-
認証抜け(セッション切れ、権限不足)
まずは「ブラウザの開発者ツール → Networkタブ」で:
-
リクエストにCookie付いてるか?
-
レスポンスにCORSヘッダーあるか?
-
ステータスコードは?(401, 403, 422あたりが怪しい)
次にGoのログ:
# 詳細ログ出力 export GIN_MODE=debug go run main.go
睡眠不足でも大丈夫なように「チェックリスト化」しておくのが、この業界で長く生きるコツです。
何か特定の部分で詰まっていたら、ぜひ教えてください。その部分の「現場あるある」をお伝えできますので。
0 件のコメント:
コメントを投稿