Cours Vibe coding avec l'IA · Module 12: Clone de Medium

Surveillance des performances - Sentry/Analytics

1 min de lecture
Dans cette leçon2

Surveillance des erreurs et des performances en production.

Configuration de Sentry

1npm install @sentry/nextjs
1// sentry.client.config.ts
2import * as Sentry from "@sentry/nextjs";
3
4Sentry.init({
5  dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
6  tracesSampleRate: 1.0,
7  environment: process.env.NODE_ENV
8});

Limites d'erreur (Error Boundaries)

1'use client';
2
3import { Component, ReactNode } from 'react';
4import * as Sentry from '@sentry/nextjs';
5
6class ErrorBoundary extends Component<
7  { children: ReactNode },
8  { hasError: boolean }
9> {
10  state = { hasError: false };
11
12  static getDerivedStateFromError() {
13    return { hasError: true };
14  }
15
16  componentDidCatch(error: Error, errorInfo: any) {
17    Sentry.captureException(error, { contexts: { react: errorInfo } });
18  }
19
20  render() {
21    if (this.state.hasError) {
22      return <div>Une erreur est survenue</div>;
23    }
24    return this.props.children;
25  }
26}

À bientôt !

Code de cette leçon : App.tsx
1'use client';
2
3import { useState } from 'react';
4
5interface Response {
6  id: string;
7  author: {
8    name: string;
9    avatar: string;
10  };
11  content: string;
12  claps: number;
13  createdAt: Date;
14  replies: Response[];
15}
16
17const mockResponses: Response[] = [
18  {
19    id: '1',
20    author: {
21      name: 'Capitaine Barbe-Rousse',
22      avatar: 'https://i.pravatar.cc/150?img=12'
23    },
24    content: 'Super article ! J\'ai vraiment aimé lire sur ce sujet. Les exemples étaient particulièrement utiles.',
25    claps: 24,
26    createdAt: new Date(Date.now() - 1000 * 60 * 60 * 2),
27    replies: [
28      {
29        id: '2',
30        author: {
31          name: 'Matelot Sam',
32          avatar: 'https://i.pravatar.cc/150?img=33'
33        },
34        content: 'Je suis d\'accord ! Les exemples pratiques ont rendu ça facile à comprendre.',
35        claps: 8,
36        createdAt: new Date(Date.now() - 1000 * 60 * 30),
37        replies: []
38      }
39    ]
40  },
41  {
42    id: '3',
43    author: {
44      name: 'Navigatrice Nancy',
45      avatar: 'https://i.pravatar.cc/150?img=45'
46    },
47    content: 'J\'adorerais voir plus de contenu comme celui-ci. Merci du partage !',
48    claps: 15,
49    createdAt: new Date(Date.now() - 1000 * 60 * 60),
50    replies: []
51  }
52];
53
54function ResponseItem({ response }: { response: Response }) {
55  return (
56    <div className="border-b border-gray-200 pb-6">
57      <div className="flex items-start gap-3">
58        <img
59          src={response.author.avatar}
60          alt={response.author.name}
61          className="w-10 h-10 rounded-full"
62        />
63        <div className="flex-1">
64          <div className="flex items-center gap-2 mb-2">
65            <span className="font-semibold">{response.author.name}</span>
66            <span className="text-sm text-gray-500">il y a 2 h</span>
67          </div>
68          <p className="text-gray-800 leading-relaxed mb-3">
69            {response.content}
70          </p>
71          <div className="flex items-center gap-4 text-sm text-gray-500">
72            <button className="flex items-center gap-1 hover:text-green-600">
73              <span>Bravo</span>
74              <span>{response.claps}</span>
75            </button>
76            <button className="hover:text-gray-700">Répondre</button>
77          </div>
78
79          {response.replies.length > 0 && (
80            <div className="mt-4 pl-6 border-l-2 border-gray-100 space-y-4">
81              {response.replies.map(reply => (
82                <ResponseItem key={reply.id} response={reply} />
83              ))}
84            </div>
85          )}
86        </div>
87      </div>
88    </div>
89  );
90}
91
92export default function ResponseSection() {
93  const [content, setContent] = useState('');
94  const [responses, setResponses] = useState(mockResponses);
95
96  const handleSubmit = () => {
97    if (!content.trim()) return;
98
99    alert(`Réponse publiée : ${content}`);
100    setContent('');
101  };
102
103  return (
104    <div className="min-h-screen bg-white">
105      <div className="max-w-2xl mx-auto px-8 py-12">
106        <h2 className="text-2xl font-bold mb-6">
107          Réponses ({responses.length})
108        </h2>
109
110        {/* Write Response */}
111        <div className="mb-8 border border-gray-200 rounded-lg p-4">
112          <textarea
113            value={content}
114            onChange={(e) => setContent(e.target.value)}
115            placeholder="Qu'en penses-tu ?"
116            className="w-full outline-none resize-none text-lg"
117            rows={3}
118          />
119          <div className="flex justify-end mt-2">
120            <button
121              onClick={handleSubmit}
122              disabled={!content.trim()}
123              className="px-6 py-2 bg-green-600 text-white rounded-full font-semibold hover:bg-green-700 disabled:bg-gray-300 disabled:cursor-not-allowed"
124            >
125              Répondre
126            </button>
127          </div>
128        </div>
129
130        {/* Responses */}
131        <div className="space-y-6">
132          {responses.map(response => (
133            <ResponseItem key={response.id} response={response} />
134          ))}
135        </div>
136      </div>
137    </div>
138  );
139}

Tu as repéré une erreur dans cette leçon ?

Teste tes connaissances

Réponds aux questions de cette leçon. Choisis une réponse pour voir tout de suite si elle est correcte.

  1. 1. À quoi sert Sentry dans une application Next.js ?

  2. 2. Quelle méthode de l'Error Boundary sert à signaler l'erreur à Sentry ?

Tâches pratiques dans le jeu

  • Éditeur de code

    Error Boundary avec envoi de rapports à Sentry

  • Tri vertical

    Ordonne les étapes de gestion d'une erreur avec un Error Boundary :

Articles utiles