Servidor-Side Rendering (SSR): Uma Ferramenta Poderosa ou um Exagero para o seu Projeto?

A escolha de tecnologias não deve ser guiada pela aparência do currículo, mas sim pela resolução de problemas concretos.

Recentemente, conversei com um ex-colega que lidera uma equipe de front-end em uma startup. Em meio à conversa, ele me perguntou: "Estamos planejando reftaorar nosso projeto para Next.js e implementar Server-Side Rendering (SSR). O que você acha?"

Deixei meus talheres de lado e respondi: "Vocês estão enfrentando algum problema específico que justifique essa refatoração?"

Ele coçou a cabeça: "Na verdade, não temos problemas graves... Mas, ao procurar emprego, percebi que todos perguntam sobre experiência com Next.js. Se não usarmos, nossa stack pode parecer desatualizada."

"E o seu produto? Precisa de otimização para SEO? Como é o ambiente de rede dos seus usuários?", indaguei.

"Nossos usuários estão em cidades de primeira linha, com boa velocidade de internet. Quanto ao SEO, o produto exige login para uso, então os motores de busca não conseguem rastrear", ele respondeu.

Olhei para ele, sem saber o que dizer. Mais um colega influenciado pelas tendências tecnológicas.

SSR não é uma bala de prata, é uma espada de dois gumes

Nos últimos anos, frameworks como Next.js e Nuxt.js ganharam imensa popularidade. Comunidades de tecnologia estão repletas de posts como "Migramos de CSR para SSR e aumentamos o FCP em 50%" ou "SSR é o futuro do front-end".

No entanto, raramente vemos alguém detalhando os desafios que a equipe enfrentará ao longo de meses para alcançar essa melhoria de performance.

1. Custos de Servidor: De Gratuito a um Investimento

Páginas puramente estáticas, hospedadas em serviços como S3 ou Netlify, podem custar muito pouco, especialmente com baixo tráfego. Com SSR:

  • Você necessita de um servidor dedicado ou instâncias de cloud functions.
  • É preciso considerar a concorrência e implementar balanceamento de carga se um único servidor não for suficiente.
  • A preocupação com a disponibilidade do servidor exige monitoramanto, alertas e estratégias de recuperação de falhas.

O projeto de um amigo, após adotar SSR, viu suas despesas mensais de nuvem saltarem de 300 para 3000. O chefe questionou se o aumento de velocidade era perceptível e se o custo adicional seria coberto pela receita. Ele não soube responder. Pior ainda, descobriu que a maioria dos usuários navegava da página inicial para detalhes, tornando a otimização da primeira renderização ineficaz para justificar o custo.

2. Transferência de Complexidade: De Front-end Puro para Full-stack

Em projetos CSR (Client-Side Rendering), o front-end foca na interface, e problemas de API são responsabilidade do back-end.

Com SSR:


// Abordagem anterior: simplicidade
function UserProfile({ userId }) {
 const [user, setUser] = useState(null);
 
 useEffect(() => {
   fetch(`/api/user/${userId}`).then(response => response.json()).then(data => setUser(data));
 }, [userId]);
 
 return <div>{user?.name}</div>;
}
 

// Com SSR: complexidade surge
export async function getServerSideProps({ params }) {
 try {
   // Gestão de timeout de API
   const controller = new AbortController();
   const timeoutId = setTimeout(() => controller.abort(), 3000);
   
   const response = await fetch(`http://internal-api/user/${params.id}`, {
     signal: controller.signal
   }).catch(() => null); // Captura erros de rede
   
   clearTimeout(timeoutId);
   
   if (!response || !response.ok) {
     // Estratégia de fallback?
     return { props: { user: null, isFallback: true } };
   }
   
   const user = await response.json();
   return { props: { user } };
 } catch (error) {
   // O que o usuário verá em caso de erro no servidor?
   console.error("Server-side fetch error:", error);
   return { notFound: true }; // Ou retornar um erro genérico
 }
}

function UserProfile({ user, isFallback }) {
 // Validação no cliente também é necessária
 const [isClient, setIsClient] = useState(false);
 
 useEffect(() => {
   setIsClient(true);
 }, []);
 
 if (!isClient) {
   // Previne erros de hidratação
   return <div>Carregando...</div>; 
 }
 
 // Verifica se o usuário existe após a hidratação
 if (!user && !isFallback) {
    return <div>Falha ao carregar perfil.</div>;
 }

 return <div>{user?.name}</div>;
}
 

Se a API falhar no servidor, qual a estratégia? Retornar erro 500 ou degradar para CSR? Como lidar com timeouts de API no servidor? Como sincronizar o estado entre servidor e cliente para evitar erros de hidratação?

Um colega que migrou para Next.js comentou: "Desde que adotei SSR, não escrevo apenas React, mas também configuro Nginx, entendo PM2 e depuro vazamentos de memória. Meu salário não aumentou, mas minhas responsabilidades dobraram." A maioria dos "likes" veio de outros desenvolvedores front-end.

3. Experiência de Desenvolvimento Fragmentada

No CSR, é comum usar window, document e localStorage livremente, pois tudo roda no navegador.

Com SSR:


// Antes, era simples
const browserWidth = window.innerWidth;
const authToken = localStorage.getItem('token');
const appHeight = document.getElementById('app').offsetHeight;
 

// Agora, requer cuidados
const browserWidth = typeof window !== 'undefined' ? window.innerWidth : 1024; // Valor padrão ou fallback
const authToken = typeof window !== 'undefined' ? localStorage.getItem('token') : null;
const [appHeight, setAppHeight] = useState(0);

useEffect(() => {
 setAppHeight(document.getElementById('app')?.offsetHeight || 0);
}, []);
 
  • Bibliotecas de terceiros que não suportam SSR precisam ser importadas dinamicamente.
  • Acesso direto a localStorage ou sessionStorage não é possível no servidor.
  • Navegação de rotas exige atenção, pois o servidor não possui o history API do navegador.

Lógicas antes triviais agora exigem código defensivo. É como passar por uma revista de segurança sem que a complexidade do negócio tenha mudado.

Você Realmente Precisa de SSR? Pergunet-se Três Questões Cruciais

Sempre que me perguntam sobre a adoção de SSR, peço que respondam a três perguntas. Na maioria das vezes, a própria reflexão os leva a desistir.

1. Seu Produto Depende de Motores de Busca?

Este é o critério mais objetivo e frequentemente usado como desculpa.

Se seu produto é:

  • Um site de conteúdo (blog, notícias, institucional).
  • Um e-commerce (páginas de produto precisam ser indexadas).

Então, SSR pode ser benéfico, pois crawlers podem ter dificuldade em executar JavaScript.

Porém, se seu produto é:

  • Um sistema de administração que requer login.
  • Uma H5 de ferramenta, jogo ou similar.
  • Uma aplicação B2B SaaS.
  • Uma versão H5 de um app social (onde o login é obrigatório).

Motores de busca não acessarão o conteúdo principal, tornando SEO uma necessidade artificial. Não use SEO como pretexto para adicionar Next.js ao seu currículo.

2. A Velocidade da Primeira Renderização é Realmente Inaceitável?

Muitas vezes, a lentidão percebida não é culpa do CSR, mas de código mal otimizado.

Analisei um projeto "lento" e descobri os seguintes problemas:


// Exemplo de código problemático
function App() {
 const [data, setData] = useState(null);
 const [user, setUser] = useState(null);
 const [config, setConfig] = useState(null);
 
 useEffect(() => {
   // Chamadas sequenciais, uma esperando a outra
   fetch('/api/data').then(res => res.json()).then(data => {
     setData(data);
     return fetch('/api/user');
   }).then(res => res.json()).then(user => {
     setUser(user);
     return fetch('/api/config');
   }).then(res => res.json()).then(setConfig);
   
   // Imagem não otimizada
   new Image().src = 'https://example.com/big-banner.png';
   
   // Script de terceiros carregado de forma síncrona
   const script = document.createElement('script');
   script.src = 'https://analytics.com/sdk.js';
   document.head.appendChild(script);
 }, []);
 
 return <div>...</div>;
}
 

Otimizar o código existente costuma ser mais vantajoso do que trocar toda a arquitetura.

No ano passado, otimizei um projeto Vue2 (CSR) que reduziu o tempo de carregamento da primeira renderização de 3.2s para 1.1s com apenas quatro mudanças:


// Após otimização
useEffect(() => {
 // 1. Chamadas em paralelo
 Promise.all([
   fetch('/api/data'),
   fetch('/api/user'),
   fetch('/api/config')
 ]).then(([dataRes, userRes, configRes]) => Promise.all([dataRes.json(), userRes.json(), configRes.json()]))
   .then(([data, user, config]) => {
     setData(data);
     setUser(user);
     setConfig(config);
   });
 
 // 2. Imagens em WebP + lazy loading
 // 3. Scripts de terceiros assíncronos
 const script = document.createElement('script');
 script.async = true;
 script.src = 'https://analytics.com/sdk.js';
 document.head.appendChild(script);
 
 // 4. Code splitting para rotas
 // const List = lazy(() => import('./pages/List')); // Exemplo com React.lazy
}, []);
 

Nenhuma mudança de arquitetura, refatoração massiva ou horas extras. Apenas código mais eficiente.

3. Sua Equipe Está Preparada?

Este é o fator mais negligenciado e a fonte de maior dor de cabeça após o lançamento.

Adotar SSR significa que sua equipe de front-end começará a escrever código de servidor:

  • Alguém tem experiência com Node.js?
  • Alguém sabe configurar Nginx?
  • Alguém consegue diagnosticar vazamentos de memória?

# Um problema surge em produção, você sabe como agir?
curl -X POST https://seu-site.com/api/user -H "Content-Type: application/json" -d '{"id":123}'

# Se a resposta for 502 Bad Gateway:
# O processo Node.js caiu? Configuração do Nginx incorreta? Timeout da API interna?

# Acessando o servidor
ssh usuario@seu-servidor
pm2 logs # Verificar logs da aplicação
df -h # Disco cheio?
free -m # Vazamento de memória?
top # CPU sobrecarregado?
 
  • Quem fará o rollback às 2 da manhã se algo der errado?

Se as respostas forem negativas, o dia do lançamento do SSR pode se tornar o início de um pesadelo para a equipe.

A escolha tecnológica deve considerar não apenas a "qualidade" da tecnologia, mas também a capacidade da equipe de adotá-la e mantê-la.

Alternativas Mais Atraentes que o SSR

Se você tem gargalos de performance, mas SSR não é a única solução, existem alternativas:

1. Static Site Generation (SSG)

Ideal para conteúdo estático ou que muda com pouca frequência.


// next.config.js (exemplo Next.js)
module.exports = {
 // Gera HTML durante o build
 exportPathMap: async function() {
   return {
     '/': { page: '/' },
     '/sobre': { page: '/about' },
     '/blog/1': { page: '/blog/[id]', query: { id: '1' } },
   };
 }
};
 
  • HTML gerado no build, implantado em CDN.
  • Performance de primeira renderização excelente e amigável para SEO.
  • Sem custos de servidor ou manutenção operacional.

Next.js, Nuxt.js e VitePress suportam SSG. Você obtém os benefícios de performance do SSR com a simplicidade de implantação do CSR.

2. Implantação Estática + CSR

Na maioria dos cenários, esta é a abordagem ideal.


// index.html (exemplo)

<html>
<head>
 <!-- Skeleton screen para feedback visual -->
 <style>
   .skeleton { background: #f0f0f0; height: 20px; margin: 10px; }
 </style>
</head>
<body>
 <div id="root">
   <div class="skeleton"></div>
   <div class="skeleton"></div>
   <div class="skeleton"></div>
 </div>
 
 <!-- Recursos em CDN para carregamento paralelo -->
 <link rel="preconnect" href="https://api.example.com">
 <script src="https://cdn.example.com/react.js" async></script>
 <script src="https://cdn.example.com/app.js" async></script>
</body>
</html>
 
  • HTML hospedado em CDN para aceleração global.
  • APIs acessadas via gateway ou BFF (Backend for Frontend).
  • Combinado com pré-carregamento, lazy loading e skeleton screens, a experiência do usuário é excelente.

Meu projeto atual usa React + Vite, compilado para estático e hospedado em S3 com CDN. A primeira renderização leva 1.2s. Com milhões de page views mensais, os custos de servidor (apenas para o BFF) são inferiores a 500 reais. Use o que é suficiente, evite complexidade desnecessária.

3. SSR Parcial (Algumas Páginas SSR, a Maioria CSR)

Se apenas algumas páginas necessitam de SEO (landing pages, site institucional) e o restante requer login, você pode aplicar SSR seletivamente.


// next.config.js (exemplo Next.js)
module.exports = {
 // Apenas estas extensões serão tratadas como páginas SSR
 pageExtensions: ['ssr.js', 'page.js'],
 
 async rewrites() {
   return [
     // Landing page usa SSR
     {
       source: '/',
       destination: '/landing.ssr.js', // Apontando para o arquivo SSR
     },
     // Outras páginas usam CSR (arquivos HTML estáticos)
     {
       source: '/app/:path*',
       destination: '/app.html', // Ou um proxy para o SPA
     }
   ];
 }
};
 

Next.js suporta o modo multi-página, e Vue oferece soluções para renderização híbrida. Não sobrecarregue 90% do seu projeto pela necessidade de 10%.

Considerações Finais: Escolha Tecnológica Pautada em Necessidade, Não em Glamour

Este artigo não visa desmerecer o SSR. É uma tecnologia valiosa quando aplicada ao contexto correto.

Contudo, critico a tendência de adoção sem critério: usar Next.js para uma simples página de evento H5, implementar SSR em um sistema de administração, ou buscar otimizar uma página de 1.5s para 1.2s.

Vale a pena sacrificar meses de manutenção da equipe por 0.3s de ganho? Os critérios para escolher uma tecnologia devem ser:

  • Resolve nossos problemas atuais?
  • A equipe tem capacidade para adotá-la e mantê-la?
  • Os custos de manutenção são sustentáveis?
  • O retorno sobre o investimento justifica o esforço?

Que a escolha tecnológica seja baseada em necessidades reais, não em uma performance para o currículo.

Compartilhe nos comentários: Que projetos "desnecessariamente SSR" você já viu? Quais armadilhas você encontrou?

Se você também se opõe a complexidade tecnológica sem propósito, siga-me para discutirmos o mundo real do front-end.

Tags: Next.js SSR SSG performance arquitetura de software

Publicado em 6-20 22:55