Alrededor de un año después de empezar con el desarrollo web, empecé a pensar que quería tener mi propio blog.
Así que estuve dedicado a esto durante unos 6 meses y me puse a crearlo.

Para las funcionalidades del front-end creo que basta con tomar como referencia el blog de Kim Do‑hyung de abajo.
A mí tampoco me llevó ni una semana crear un blog usando mdx.
De hecho, para gestionar un blog, con solo el front-end no había grandes problemas.
Durante un tiempo pensé que bastaba con crear mdx y desarrollar solo el front-end, pero no pude renunciar al sueño de un blog full‑stack con el que poder escribir desde cualquier lugar con acceso a la web.
1. Funciones
Las funciones no tienen nada de especial.
Se puede publicar artículos y consultarlos.

Al iniciar sesión, la forma del botón de la parte superior derecha cambia.
Y al hacer clic se pueden subir artículos.
En el vídeo de abajo se ve el proceso de escribir y subir un artículo.
Hay un proceso de conversión de imágenes, y todas las imágenes subidas se gestionan en formato avif, que es el más eficiente.
En el caso de YouTube, basta con pegar la URL y se puede insertar como arriba.
La idea de la función de gestión de tablas la tomé de Tistory.

Cuando el foco se pone sobre la tabla, aparece en la parte superior una barra de herramientas para añadir o eliminar tablas.
Por supuesto, también es posible el guardado temporal.
Dentro del editor, al pulsar cmd+s o ctrl+s se guarda.
2. Arquitectura
“Arquitectura” suena muy elegante, pero en coreano es simplemente estructura.
La primera petición que entra al servidor la distribuye Caddy como reverse proxy.
Si solo tuviera front‑end, convertiría directamente el mdx y lo enviaría, pero como hice un back‑end, lo conecté con Next.js y Prisma.

Como todo el mundo sabe, si metes en el back‑end los datos que vienen del usuario tal cual, te hackean al instante.
Hay que validarlos mediante la API antes de guardarlos.

Una vez, con el deseo de que se dejaran muchos comentarios en el blog, dejé la API abierta para que cualquiera pudiera comentar.
Entonces un tipo empezó a lanzar ataques de inyección de consultas a esa API una y otra vez, y acabó habiendo unos 100 comentarios.
También hubo uno que intentó hackear insertando fetch y comandos dentro de una imagen svg.
Puede que en algún lugar de China exista otro “yo” andando tan campante sin que yo lo sepa.
3. Editor

Lo que hice con más esmero fue el editor.
El editor lo desarrollé añadiendo varias funciones a Tiptap.
Por eso solo los componentes relacionados con Tiptap superan la decena.

En el caso de la inserción de imágenes, lo implementé como se muestra a continuación.
const editor = useEditor({
extensions: editorExtensions,
content,
onUpdate: ({ editor }) => {
onChange(editor.getHTML());
},
editorProps,
immediatelyRender: false,
});
const { uploadProgress, uploadImage, clearProgress } = useImageUpload({
onUploadComplete: (imageUrl, isAnimated) => {
if (editor) {
try {
editor
.chain()
.focus()
.insertContent([
{
type: "image",
attrs: {
src: imageUrl,
alt: "업로드된 이미지",
"data-animated": isAnimated || false,
},
},
{
type: "paragraph",
content: [],
},
])
.run();
} catch (error) {
console.error("이미지 삽입 오류:", error);
}
}
},
});Para obtener ventajas en cuanto a rendimiento, guardé y cargué el HTML tal cual.
Al guardar desde Tiptap hacia el back‑end añadí varios pasos.
Tras procesar por adelantado distintos elementos como bloques de código, Google Maps, títulos, imágenes, vistas previas de enlaces, etc., guardé por separado content y processedContent.
4. Esquema de back‑end
Recientemente, al implementar un servicio multilingüe, me di cuenta de que cambiar un esquema de back‑end una vez creado es un desastre.
Me vinieron a la mente las palabras del profesor que remarcaba la importancia del esquema.
Si estás pensando incluso en el back‑end del blog, te recomiendo que reflexiones largo y tendido sobre qué servicios ofrecerás.
Al ir añadiendo esto y lo otro, el esquema se volvió demasiado grande, y aunque me da algo de vergüenza, lo hago público.
model Post {
id String @id @default(cuid())
title String
slug Int @unique @default(autoincrement())
excerpt String
content String
processedContent String?
thumbnail String?
published Boolean @default(false)
createdAt DateTime @default(now())
updatedAt DateTime?
likes Int @default(0)
views Int @default(0)
comments Comment[]
likesRows PostLike[]
viewDedups PostViewDedup[]
notifications Notification[]
locales PostLocale[]
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
category String
@@map("posts")
}5. Conclusiones
Ahora toca responder a la siguiente pregunta.
¿Es necesario llegar hasta el back‑end para crear un blog?
Una vez terminado, me vino a la cabeza: ¿era realmente necesario?
Al final me di cuenta de que WordPress era un camino muchísimo más fácil.
Desde el momento en que implementas el back‑end y las APIs, empiezan a aparecer un montón de cosas en las que pensar.
Durante el reciente incidente de react2shell, recibía ataques cada pocos minutos, y en la terminal aparecían constantemente comandos que nunca había visto, así que pasé varios días duros.
Incluso ahora, cada pocos días paso npm audit para revisar y corregir aspectos de seguridad.
A veces, cuando cambio el esquema de la base de datos y la migración se lía, me entra un sudor frío.
Aun así, mirando atrás, creo que fue positivo haber aprendido muchas cosas al implementar el back‑end y haber conseguido un blog en el que puedo implementar funciones con mis propias manos.
De ahora en adelante, más que añadir funciones al blog, quiero ir realizando otros proyectos para enriquecer mi portafolio.








댓글을 불러오는 중...