#การทำ Code Review ด้วย SonarQube สำหรับ React และ Laravel

การตรวจสอบคุณภาพโค้ดก่อนนำขึ้นระบบจริงไม่ควรพึ่งเฉพาะการอ่านโค้ดโดยนักพัฒนาเท่านั้น เพราะปัญหาหลายประเภท เช่น โค้ดซ้ำ ความซับซ้อน ช่องโหว่ หรือ Code Smell สามารถตรวจจับแบบอัตโนมัติได้

SonarQube เป็นแพลตฟอร์มสำหรับทำ Static Code Analysis และ Automated Code Review โดยสามารถนำไปใช้ในกระบวนการพัฒนา React และ Laravel รวมถึงเชื่อมต่อกับ CI/CD เพื่อกำหนดว่าโค้ดที่ไม่ผ่านมาตรฐานจะไม่สามารถผ่าน Quality Gate ได้

SonarQube ไม่ได้แทนที่ Human Code Review แต่ทำหน้าที่เป็น Automated Reviewer เพื่อช่วยตรวจจับปัญหาที่สามารถตรวจสอบด้วยกฎและการวิเคราะห์แบบ Static Analysis ก่อนให้นักพัฒนาตรวจสอบ Design, Business Logic และ Architecture ต่อไป


#1. SonarQube ตรวจสอบอะไรได้บ้าง

SonarQube วิเคราะห์ Source Code แล้วรายงานประเด็นด้านคุณภาพและความปลอดภัย เช่น

  • Reliability
  • Maintainability
  • Security
  • Security Hotspots
  • Duplicated Code
  • Complexity
  • Test Coverage
  • Coding Standard
  • Technical Debt

แนวคิดโดยรวมสามารถเขียนเป็น Pipeline ได้ดังนี้

Developer
   │
   ▼
Git Push / Pull Request
   │
   ▼
Unit Test + Coverage
   │
   ▼
SonarScanner
   │
   ▼
SonarQube Server
   │
   ├── Reliability
   ├── Security
   ├── Maintainability
   ├── Duplication
   └── Coverage
   │
   ▼
Quality Gate
   │
   ├── PASS → Build / Deploy
   └── FAIL → แก้ไขโค้ด

#2. React และ Laravel ใช้กับ SonarQube ได้หรือไม่

ได้

SonarQube รองรับการวิเคราะห์ JavaScript และ TypeScript และระบุ React JSX เป็นหนึ่งใน Framework/Tool ที่รองรับ

สำหรับฝั่ง Backend ตัว PHP Analyzer รองรับ Laravel โดยตรง

ดังนั้น Full Stack Application ที่ใช้

React
  +
Laravel API
  +
SonarQube

สามารถนำทั้ง Frontend และ Backend เข้าสู่กระบวนการตรวจสอบคุณภาพโค้ดได้


#3. ติดตั้ง SonarQube ด้วย Docker Compose

สำหรับเครื่อง Development สามารถรัน SonarQube Community Build ด้วย Docker Compose ได้

สร้างไฟล์

docker-compose.yml

ตัวอย่าง

services:

  sonarqube:
    image: sonarqube:community
    container_name: sonarqube
    depends_on:
      - db
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonar
      SONAR_JDBC_PASSWORD: sonar
    ports:
      - "9000:9000"
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_extensions:/opt/sonarqube/extensions
      - sonarqube_logs:/opt/sonarqube/logs

  db:
    image: postgres:17-alpine
    container_name: sonarqube-db
    environment:
      POSTGRES_USER: sonar
      POSTGRES_PASSWORD: sonar
      POSTGRES_DB: sonarqube
    volumes:
      - postgresql_data:/var/lib/postgresql/data

volumes:
  sonarqube_data:
  sonarqube_extensions:
  sonarqube_logs:
  postgresql_data:

รัน

docker compose up -d

ตรวจสอบ Container

docker compose ps

จากนั้นเปิด

http://localhost:9000

สำหรับการติดตั้งครั้งแรกให้เข้าสู่ระบบและเปลี่ยนรหัสผ่านผู้ดูแลระบบตามขั้นตอนของ SonarQube

สำหรับ Production ควร Pin Docker Image เป็น Version ที่ต้องการแทนการอ้างอิง Tag แบบลอย และควรใช้ Database, Backup, TLS/Reverse Proxy และ Resource Configuration ที่เหมาะสมกับระบบจริง


#4. สร้าง Project และ Token

ใน SonarQube ให้สร้าง Project เช่น

react-app
laravel-api

หรือถ้าเป็น Monorepo อาจสร้างเป็น

fullstack-app

จากนั้นสร้าง Analysis Token

ตัวอย่าง

SONAR_TOKEN=sqp_xxxxxxxxxxxxxxxxx

ไม่ควรเขียน Token ลงใน Source Code หรือ Commit ลง Git

แนะนำให้เก็บเป็น

Environment Variable

หรือ

GitHub Actions Secrets

#5. การตรวจสอบ React ด้วย SonarQube

สมมติ Project

react-app/
├── src/
├── public/
├── coverage/
├── package.json
├── vite.config.ts
└── sonar-project.properties

#5.1 สร้าง Test Coverage

SonarQube ไม่ได้รัน Unit Test ให้เรา แต่จะอ่าน Coverage Report ที่สร้างจาก Test Framework

ตัวอย่าง React + Vitest

ติดตั้ง

npm install -D vitest @vitest/coverage-v8

ใน package.json

{
  "scripts": {
    "test": "vitest",
    "test:coverage": "vitest run --coverage"
  }
}

ตัวอย่าง vite.config.ts

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  test: {
    coverage: {
      provider: 'v8',
      reporter: ['text', 'lcov']
    }
  }
})

รัน

npm run test:coverage

จะได้ไฟล์ เช่น

coverage/lcov.info

SonarQube ใช้ LCOV Report สำหรับ Coverage ของ JavaScript/TypeScript


#5.2 สร้าง sonar-project.properties สำหรับ React

สร้าง

sonar-project.properties

ตัวอย่าง

sonar.projectKey=react-app
sonar.projectName=React App

sonar.sources=src

sonar.tests=src
sonar.test.inclusions=**/*.test.js,**/*.test.jsx,**/*.test.ts,**/*.test.tsx,**/*.spec.js,**/*.spec.jsx,**/*.spec.ts,**/*.spec.tsx

sonar.exclusions=**/*.test.js,**/*.test.jsx,**/*.test.ts,**/*.test.tsx,**/*.spec.js,**/*.spec.jsx,**/*.spec.ts,**/*.spec.tsx,**/node_modules/**,**/dist/**,**/coverage/**

sonar.javascript.lcov.reportPaths=coverage/lcov.info

sonar.sourceEncoding=UTF-8

#5.3 รัน SonarScanner

กำหนด Environment Variable

Linux/macOS

export SONAR_HOST_URL=http://localhost:9000
export SONAR_TOKEN=sqp_xxxxxxxxx

จากนั้น

sonar-scanner

เมื่อ Scan เสร็จให้เปิด SonarQube Dashboard

React App
 ├── Issues
 ├── Security
 ├── Reliability
 ├── Maintainability
 ├── Coverage
 └── Duplications

#6. ตัวอย่างปัญหา React ที่ SonarQube ช่วยตรวจพบ

ตัวอย่าง

function UserProfile({ user }: any) {

  if (user != null) {
    console.log(user)
  }

  return (
    <div>
      {user.name}
    </div>
  )
}

ประเด็นที่ Automated Code Review อาจช่วยชี้ให้เห็นขึ้นอยู่กับ Rule/Profile ที่เปิดใช้ เช่น

  • ใช้ any มากเกินไป
  • Logic ที่อาจทำให้เกิด Null/Undefined Problem
  • Debug/Console Code ที่ไม่ควรอยู่ใน Production
  • Code Smell
  • Complexity
  • Duplicate Logic

แนวทางปรับปรุงอาจเป็น

type User = {
  id: number
  name: string
}

type Props = {
  user?: User
}

export function UserProfile({ user }: Props) {

  if (!user) {
    return <p>User not found</p>
  }

  return (
    <div>
      {user.name}
    </div>
  )
}

#7. การตรวจสอบ Laravel ด้วย SonarQube

ตัวอย่าง Project

laravel-api/
├── app/
├── bootstrap/
├── config/
├── database/
├── routes/
├── tests/
├── vendor/
├── composer.json
└── sonar-project.properties

#7.1 สร้าง PHPUnit Coverage

Laravel สามารถใช้ PHPUnit สร้าง Clover XML Coverage Report ได้

เครื่องที่รัน Coverage ต้องมี Coverage Driver เช่น Xdebug หรือ PCOV

ตัวอย่าง

./vendor/bin/phpunit --coverage-clover coverage.xml

จะได้

coverage.xml

SonarQube สามารถอ่าน PHP Coverage ในรูปแบบ Clover XML


#7.2 sonar-project.properties สำหรับ Laravel

sonar.projectKey=laravel-api
sonar.projectName=Laravel API

sonar.sources=app,routes,config,database
sonar.tests=tests

sonar.exclusions=vendor/**,storage/**,bootstrap/cache/**,public/build/**,node_modules/**

sonar.php.coverage.reportPaths=coverage.xml

sonar.sourceEncoding=UTF-8

จากนั้นรัน

sonar-scanner

#8. ตัวอย่างปัญหา Laravel ที่ควรตรวจสอบ

ตัวอย่าง Controller

public function show($id)
{
    $user = DB::select(
        "SELECT * FROM users WHERE id = " . $id
    );

    return response()->json($user);
}

Code ลักษณะนี้ควรได้รับการตรวจสอบอย่างจริงจัง เพราะการต่อ SQL String กับ Input จากภายนอกสามารถสร้างความเสี่ยงด้าน Security ได้

แนวทางที่ปลอดภัยกว่า เช่นใช้ Eloquent

public function show(int $id)
{
    $user = User::findOrFail($id);

    return response()->json($user);
}

หรือใช้ Query Builder/Parameterized Query ตามความเหมาะสม


#9. React + Laravel แบบ Monorepo

ตัวอย่าง

fullstack-app/
├── frontend/
│   ├── src/
│   ├── coverage/
│   └── package.json
│
├── backend/
│   ├── app/
│   ├── routes/
│   ├── tests/
│   └── coverage.xml
│
└── sonar-project.properties

สามารถ Scan ทั้งสองภาษาใน Project เดียวได้

ตัวอย่าง

sonar.projectKey=fullstack-app
sonar.projectName=React Laravel Full Stack

sonar.sources=frontend/src,backend/app,backend/routes,backend/config
sonar.tests=frontend/src,backend/tests

sonar.test.inclusions=frontend/src/**/*.test.js,frontend/src/**/*.test.jsx,frontend/src/**/*.test.ts,frontend/src/**/*.test.tsx,frontend/src/**/*.spec.js,frontend/src/**/*.spec.jsx,frontend/src/**/*.spec.ts,frontend/src/**/*.spec.tsx,backend/tests/**

sonar.exclusions=frontend/src/**/*.test.js,frontend/src/**/*.test.jsx,frontend/src/**/*.test.ts,frontend/src/**/*.test.tsx,frontend/src/**/*.spec.js,frontend/src/**/*.spec.jsx,frontend/src/**/*.spec.ts,frontend/src/**/*.spec.tsx,frontend/node_modules/**,frontend/dist/**,frontend/coverage/**,backend/vendor/**,backend/storage/**,backend/bootstrap/cache/**

sonar.javascript.lcov.reportPaths=frontend/coverage/lcov.info
sonar.php.coverage.reportPaths=backend/coverage.xml

sonar.sourceEncoding=UTF-8

Workflow คือ

React Tests
      │
      └── coverage/lcov.info
              │
              ▼
         SonarScanner
              ▲
              │
      ┌───────┘
      │
Laravel Tests
      │
      └── coverage.xml

SonarQube จะรวมข้อมูล Static Analysis และ Coverage ของหลายภาษาไว้ใน Dashboard ของ Project เดียว


#10. Quality Gate คืออะไร

Quality Gate คือเงื่อนไขที่ใช้ตัดสินว่า Code Quality ผ่านเกณฑ์หรือไม่

แนวคิดตัวอย่าง

Coverage >= 80%
Duplicated Code <= 3%
New Critical Issues = 0
Security Hotspots ต้องได้รับการ Review

SonarQube มี Quality Gate มาตรฐานของระบบ และสามารถสร้าง Custom Quality Gate ให้เหมาะกับทีมได้

แนวทางที่แนะนำคือให้ความสำคัญกับ New Code ก่อน เพื่อป้องกันไม่ให้ Technical Debt ใหม่เพิ่มเข้ามาในระบบ


#11. Quality Profile

Quality Profile คือชุดของ Rules สำหรับแต่ละภาษา

เช่น

JavaScript / TypeScript
 ├── Reliability Rules
 ├── Security Rules
 └── Maintainability Rules

PHP
 ├── Reliability Rules
 ├── Security Rules
 └── Maintainability Rules

ทีมสามารถสร้าง Custom Quality Profile เพื่อเปิด/ปิด Rules ให้สอดคล้องกับ Coding Standard ขององค์กรได้


#12. ใช้ SonarQube กับ GitHub Actions

ตัวอย่าง Workflow สำหรับ Repository ที่มี

frontend = React
backend  = Laravel

สร้าง

.github/workflows/sonarqube.yml
name: SonarQube Analysis

on:
  push:
    branches:
      - main

jobs:

  sonar:

    runs-on: ubuntu-latest

    steps:

      - name: Checkout
        uses: actions/checkout@v6
        with:
          fetch-depth: 0

      - name: Setup Node
        uses: actions/setup-node@v6
        with:
          node-version: 22
          cache: npm
          cache-dependency-path: frontend/package-lock.json

      - name: Install React dependencies
        working-directory: frontend
        run: npm ci

      - name: React tests and coverage
        working-directory: frontend
        run: npm run test:coverage

      - name: Setup PHP
        uses: shivammathur/setup-php@v2
        with:
          php-version: '8.4'
          coverage: xdebug

      - name: Install Laravel dependencies
        working-directory: backend
        run: composer install --no-interaction --prefer-dist

      - name: Laravel tests and coverage
        working-directory: backend
        run: ./vendor/bin/phpunit --coverage-clover coverage.xml

      - name: SonarQube Scan
        uses: SonarSource/sonarqube-scan-action@v8
        env:
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
          SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}

กำหนด GitHub Secrets

SONAR_TOKEN
SONAR_HOST_URL

ตัวอย่าง

SONAR_HOST_URL=https://sonarqube.example.com

หาก SonarQube อยู่ใน Private Network ที่ GitHub-hosted Runner เข้าถึงไม่ได้ ควรใช้ Self-hosted Runner หรือออกแบบ Network Access ที่ปลอดภัย ไม่ควรเปิด SonarQube ออกอินเทอร์เน็ตโดยไม่มี TLS, Authentication และ Network Protection ที่เหมาะสม


#13. ทำให้ CI/CD หยุดเมื่อ Quality Gate ไม่ผ่าน

สามารถกำหนดให้ Scanner รอผล Quality Gate ได้ เช่น

sonar.qualitygate.wait=true

แนวคิด Pipeline จะเป็น

Push Code
   │
   ▼
Test
   │
   ▼
Coverage
   │
   ▼
SonarQube Scan
   │
   ▼
Quality Gate
   │
   ├── PASS ──► Build / Deploy
   │
   └── FAIL ──► Stop Pipeline

ช่วยป้องกัน Code ที่ไม่ผ่านเกณฑ์เข้าสู่ Production


#14. Community Build กับ Pull Request Review

SonarQube Community Build เหมาะสำหรับการวิเคราะห์ Main Branch และเริ่มต้นใช้งาน Automated Code Review

หากต้องการความสามารถระดับ

Feature Branch Analysis
Pull Request Analysis
Pull Request Decoration

ควรตรวจสอบ Edition ของ SonarQube ที่ใช้งาน เพราะความสามารถในการวิเคราะห์หลาย Branch และ Pull Request เป็นความสามารถของ SonarQube Server ระดับสูงกว่าชุด Community Build

ดังนั้นในการสอนหรือทดลองสามารถเริ่มจาก

SonarQube Community Build
        +
main branch analysis

จากนั้นจึงขยายไปสู่ PR-based Quality Gate เมื่อระบบต้องการ Workflow ที่เข้มขึ้น


#15. SonarQube ไม่ได้แทน Human Code Review

สิ่งที่ SonarQube เหมาะกับการตรวจสอบ

Syntax / Static Pattern
Code Smell
Security Rule
Duplication
Complexity
Coverage
Coding Standard

แต่ Human Reviewer ยังควรตรวจ

Business Logic
Architecture
Domain Model
API Design
UX
Naming Context
Performance Architecture
Maintainability เชิงระบบ
ความถูกต้องตาม Requirement

แนวทางที่เหมาะสมคือ

Automated Review
      +
Human Review
      +
Automated Test

#16. Workflow ที่แนะนำสำหรับ React + Laravel

Developer
   │
   ▼
Feature Development
   │
   ├── React
   └── Laravel
   │
   ▼
Lint
   │
   ▼
Unit Test
   │
   ├── Vitest/Jest
   └── PHPUnit/Pest
   │
   ▼
Coverage Report
   │
   ├── LCOV
   └── Clover XML
   │
   ▼
SonarScanner
   │
   ▼
SonarQube
   │
   ▼
Quality Gate
   │
   ├── PASS
   │      │
   │      ▼
   │   Human Review
   │      │
   │      ▼
   │    Merge
   │
   └── FAIL
          │
          ▼
       Fix Code

#17. แนวทางใช้งานในทีมพัฒนา

สำหรับทีมขนาดเล็ก

Developer
↓
Push
↓
CI Test
↓
SonarQube
↓
Merge

สำหรับทีมที่ต้องการ Quality Control มากขึ้น

Developer
↓
Pull Request
↓
Automated Test
↓
SonarQube Analysis
↓
Quality Gate
↓
Human Code Review
↓
Merge
↓
Deploy

#18. Checklist

ก่อน Merge Code สามารถใช้ Checklist ดังนี้

[ ] Unit Test ผ่าน
[ ] Coverage Report ถูกสร้าง
[ ] SonarQube Scan สำเร็จ
[ ] Quality Gate ผ่าน
[ ] ไม่มี Issue ระดับสำคัญที่ยังไม่ได้จัดการ
[ ] Security Hotspot ได้รับการ Review
[ ] Duplicated Code อยู่ในเกณฑ์
[ ] New Code Coverage ผ่านเกณฑ์
[ ] Human Code Review ผ่าน

#19. สรุป

การนำ SonarQube มาใช้กับ React และ Laravel ช่วยเพิ่ม Automated Quality Control ให้กับกระบวนการพัฒนาซอฟต์แวร์

โครงสร้างที่แนะนำคือ

React
  │
  ├── Vitest/Jest
  └── LCOV
       │
       ▼
   SonarQube
       ▲
       │
Laravel
  │
  ├── PHPUnit/Pest
  └── Clover XML

จากนั้นใช้ Quality Gate เป็นเกณฑ์ควบคุมก่อน Merge หรือ Deploy

แนวคิดสำคัญที่สุดไม่ใช่การพยายามแก้ Technical Debt เก่าทั้งหมดในครั้งเดียว แต่คือการทำให้ New Code มีคุณภาพ และไม่เพิ่มปัญหาใหม่เข้าไปในระบบ


#References