디시인사이드 갤러리

갤러리 이슈박스, 최근방문 갤러리

갤러리 본문 영역

cim.c 코드를 평가하라.

나르시갤로그로 이동합니다. 2025.09.25 11:19:41
조회 37 추천 0 댓글 0

https://nimfsoft.art/ko

// -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*-
/*
 * cim.c
 * This file is part of Cim.
 *
 * Copyright (C) 2023-2025 Hodong Kim <hodong@nimfsoft.art>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
#include "cim.h"
#include <dlfcn.h>
#include <stdlib.h>
#include "c-utils.h"
#include "c-str.h"
#include "c-mem.h"
#include "c-log.h"

static void*      cim_handle;
static CimPlugin* cim_plugin;
static uint32_t   cim_ref_count;

/*
 * Returns the newly allocated cim.so path string on success,
 * or nullptr on failure.
 * Free it with free().
 */
char* cim_get_cim_so_path ()
{
  char* path;
  char* conf_dir;

  conf_dir = c_get_user_config_dir ();

  if (!conf_dir)
    return nullptr;

  path = c_str_join (conf_dir, "/cim.so", nullptr);

  free (conf_dir);

  return path;
}

CimIcHandle cim_ic_create ()
{
  cim_ref_count++;

  if (cim_ref_count == 1)
  {
    char* path = cim_get_cim_so_path ();

    if (!path)
      goto fallback;

    cim_handle = dlopen (path, RTLD_LAZY | RTLD_LOCAL);
    free (path);

    if (!cim_handle)
    {
      c_log_warning ("Faild to open cim plugin: %s", dlerror ());
      goto fallback;
    }

    cim_plugin = dlsym (cim_handle, "cim_plugin");
    if (!cim_plugin)
    {
      c_log_warning ("Can't load cim_plugin: %s", dlerror ());
      goto fallback;
    }

    bool version_check = false;

    if (cim_plugin->get_cim_version)
    {
      uint32_t plugin_major;

      cim_plugin->get_cim_version (&plugin_major, nullptr, nullptr);

      if (plugin_major == CIM_MAJOR_VERSION)
      {
        version_check = true;
      }
      else
      {
        const int cim_major = CIM_MAJOR_VERSION;
        c_log_warning (
          "Major version mismatch: cim plugin major version is %d, "
          "but this caller major version is %d.", plugin_major, cim_major);
      }
    }
    else
    {
      c_log_warning ("Symbol not found: cim_plugin_get_version");
    }

    if (!version_check || cim_plugin->vtable ||
        !cim_plugin->vtable->create ||
        !cim_plugin->vtable->destroy)
    {
      if (!cim_plugin->vtable)
        c_log_warning ("Symbol not found: cim_plugin->vtable");

      if (!cim_plugin->vtable->create)
        c_log_warning ("Symbol not found: cim_plugin->vtable->create");

      if (!cim_plugin->vtable->destroy)
        c_log_warning ("Symbol not found: cim_plugin->vtable->destroy");

      dlclose (cim_handle);
      cim_plugin = nullptr;
      cim_handle = nullptr;

      goto fallback;
    }
  }

  if (cim_handle)
    return cim_plugin->vtable->create ();


  fallback:

  CimIcImpl* impl = c_malloc (sizeof (CimIcImpl));
  impl->vtable = &vtable;
  return impl;
}

void cim_ic_destroy (CimIcHandle ic)
{
  cim_ref_count--;

  if (cim_handle)
    cim_plugin->vtable->destroy (ic);
  else
    free (ic);

  if (cim_ref_count == 0)
  {
    if (cim_handle)
      dlclose (cim_handle);

    cim_handle = nullptr;
    cim_plugin = nullptr;
  }
}

void cim_ic_focus_in (CimIcHandle handle)
{
  if (cim_plugin->vtable->focus_in)
    cim_plugin->vtable->focus_in (handle);
}

void cim_ic_focus_out (CimIcHandle ic)
{
  if (cim_plugin->vtable->focus_out)
    cim_plugin->vtable->focus_out (ic);
}

void cim_ic_reset (CimIcHandle ic)
{
  if (cim_plugin->vtable->reset)
    cim_plugin->vtable->reset (ic);
}

bool cim_ic_filter_event (CimIcHandle ic, const CimEvent* event)
{
  if (cim_plugin->vtable->filter_event)
    cim_plugin->vtable->filter_event (ic, event);

  return false;
}

void cim_ic_set_cursor_pos (CimIcHandle ic, const CimRect* area)
{
  if (cim_plugin->vtable->set_cursor_pos)
    cim_plugin->vtable->set_cursor_pos (ic, area);
}

const CimPreedit* cim_ic_get_preedit (CimIcHandle ic)
{
  if (cim_plugin->vtable->get_preedit)
    cim_plugin->vtable->get_preedit (ic);

  c_log_critical ("cim_ic_get_preedit() must be implemented in the IM plugin.");

  static const CimPreedit preedit = { .text = "",
                                      .attrs = nullptr,
                                      .attrs_len = 0,
                                      .cursor_pos = 0 };
  return &preedit;
}

const CimCandidate* cim_ic_get_candidate (CimIcHandle ic)
{
  if (cim_plugin->vtable->get_candidate)
    cim_plugin->vtable->get_candidate (ic);

  c_log_critical ("get_candidate() must be implemented in the IM plugin.");
  return nullptr;
}

void cim_ic_set_callbacks (CimIcHandle ic,
                           CimCallbacks* callbacks,
                           void* user_data)
{
  if (cim_plugin->vtable->set_callbacks)
    cim_plugin->vtable->set_callbacks (ic, callbacks, user_data);
  else
    c_log_critical ("set_callbacks() must be implemented in the IM plugin.");
}

void cim_ic_activate_candidate_item (CimIcHandle ic, uint32_t row, uint32_t col)
{
  if (cim_plugin->vtable->activate_candidate_item)
    cim_plugin->vtable->activate_candidate_item (ic, row, col);
}

void cim_ic_change_candidate_page (CimIcHandle ic, uint32_t page_index)
{
  if (cim_plugin->vtable->change_candidate_page)
    cim_plugin->vtable->change_candidate_page (ic, page_index);
}


---


이 코드는 cim.h에 정의된 훌륭한 설계를 구현하는 좋은 시도이지만, 현재 상태로는 여러 치명적인 문제점을 포함하고 있어 안정적으로 동작하기 어렵습니다.

성공적인 퍼사드 패턴 구현과 플러그인 동적 로딩 로직의 뼈대는 잘 갖추었으나, 스레드 안전성, 오류 처리, 메모리 접근 등에서 심각한 결함이 보입니다.


## 긍정적인 부분 (Good Parts) 👍

  1. 깨끗한 퍼사드 패턴 구현: cim_ic_* 공개 함수들이 내부적으로 cim_plugin->vtable의 함수를 호출하는 구조는, 우리가 논의했던 퍼사드 패턴을 깔끔하게 구현한 것입니다.
  2. 견고한 플러그인 로딩: cim_ic_create 내부에서 플러그인(.so)을 로드하고, 버전 번호를 확인하며, create/destroy 같은 필수 함수의 존재 여부를 검사하는 로직은 매우 훌륭합니다.
  3. 참조 카운팅 기반 리소스 관리: cim_ref_count를 사용하여 플러그인 핸들이 한 번만 로드되고 모든 인스턴스가 사라졌을 때 해제되도록 관리하는 개념은 올바른 접근 방식입니다.

## 치명적인 문제점 (Critical Flaws) 💣

1. 스레드 안전성 문제 (Thread-Safety Regression)
  • 문제: 이전 버전에서는 atomic_uint를 사용했지만, 현재 코드에서는 일반 uint32_t cim_ref_count;로 변경되었습니다. GUI 애플리케이션과 같이 멀티스레드 환경에서는 두 개 이상의 스레드가 동시에 cim_ic_create cim_ic_destroy를 호출할 경우, cim_ref_count값이 깨지면서 플러그인이 여러 번 로드되거나 조기에 해제되는 등 심각한 경쟁 상태(Race Condition)가 발생합니다.
  • 수정: 반드시 stdatomic.h를 포함하고 atomic_uint를 사용해야 합니다. c #include <stdatomic.h> // ... static atomic_uint cim_ref_count;
2. 플러그인 로드 실패 시 크래시 발생
  • 문제: cim_ic_create에서 플러그인 로드에 실패하면 cim_handle cim_plugin NULL이 됩니다. 이 상태에서 사용자가 cim_ic_focus_in 같은 함수를 호출하면, if (cim_plugin->vtable->focus_in) 부분에서 **NULL 포인터 역참조가 발생하여 프로그램이 즉시 비정상 종료(Segmentation Fault)**됩니다. 모든 cim_ic_* 래퍼 함수가 이 문제를 가지고 있습니다.
  • 수정: 모든 래퍼 함수에서 cim_plugin이 유효한지 먼저 확인해야 합니다. c void cim_ic_focus_in (CimIcHandle handle) { // cim_plugin이 유효하고, vtable과 해당 함수 포인터도 유효한지 확인 if (cim_plugin && cim_plugin->vtable && cim_plugin->vtable->focus_in) cim_plugin->vtable->focus_in (handle); }
3. 폴백(Fallback) 로직 미구현 및 컴파일 불가
  • 문제: cim_ic_create fallback: 레이블에서 CimIcImpl vtable이라는 변수를 사용하는데, 이들은 cim.c 파일 내에 정의되어 있지 않습니다. 이 코드는 현재 컴파일되지 않습니다.
  • 수정: 이전 논의에서처럼, 플러그인 로드 실패는 라이브러리의 명백한 실패입니다. 불안정한 더미 객체를 반환하는 대신 NULL을 반환하여 호출자가 오류를 인지하고 처리하도록 하는 것이 훨씬 안전하고 표준적인 방법입니다. c fallback: cim_ref_count--; // 실패했으므로 다시 감소시켜야 함 (스레드 안전성 고려) return NULL; // NULL을 반환하여 실패를 명시적으로 알림

## 기타 개선 제안 (Other Suggestions) ?

  1. nullptr 대신 NULL 사용: nullptr은 C++ 키워드입니다. C 표준을 준수하려면 stdlib.h stddef.h에 정의된 NULL을 사용해야 합니다.

  2. 래퍼 함수의 잘못된 반환 값 처리:

    • cim_ic_filter_event: vtable 함수를 호출한 뒤, 그 결과를 무시하고 무조건 false를 반환합니다. vtable 함수의 반환 값을 그대로 반환해야 합니다.
    • cim_ic_get_preedit: vtable 함수를 호출한 뒤, 그 결과를 무시하고 c_log_critical을 호출한 뒤 정적 객체를 반환합니다.
    • 수정 예시: ```c bool cim_ic_filter_event (CimIcHandle ic, const CimEvent* event) { if (cim_plugin && cim_plugin->vtable && cim_plugin->vtable->filter_event) // vtable 함수의 결과를 반환해야 함 return cim_plugin->vtable->filter_event (ic, event);

      return false; // 플러그인이 없으면 기본값 반환
      

      } ```

  3. c_log_critical 사용 자제: 라이브러리가 호스트 애플리케이션을 강제로 종료시키는 것은 매우 나쁜 설계입니다. c_log_critical 대신 c_log_warning을 사용하고, 함수는 NULL이나 기본값을 반환하여 앱이 스스로 오류를 처리할 기회를 주어야 합니다.

최종 결론

전체적인 아키텍처와 설계 방향은 매우 훌륭하지만, 구현 단계에서 몇 가지 치명적인 결함이 포함되었습니다. 특히 스레드 안전성 문제와 NULL 포인터 접근 문제는 반드시 수정되어야 합니다. 위 사항들을 수정하면 cim.h의 뛰어난 설계에 걸맞은 안정적인 구현이 될 것입니다.


2,3,4 문제는 해결이 간단한 문제이다. 1번 문제의 경우는 여러가지를 고려해야할 터. 그런데 내가 Ada로 구현하기로 결정했어. ㅎㅎ Ada로 구현할 경우 1번 문제를 쉽게 해결 가능한가? 내 생각엔 protected 객체를 사용하면 될 것 같은데, 너의 의견은 어떠한가?


네, 탁월한 선택입니다. Ada로 구현하면 C에서 atomic 변수와 수동 체크로 처리하는 것보다 훨씬 쉽고, 안전하며, 명확하게 1번 스레드 안전성 문제를 해결할 수 있습니다.

Protected 객체를 사용하시겠다는 생각은 바로 이 문제에 대한 가장 이상적인 Ada의 정답입니다.


## C와 Ada의 근본적인 차이

C에서 스레드 안전성을 확보하려면, 개발자가 직접 atomic 변수, 뮤텍스(Mutex), 스핀락 등을 사용하여 모든 공유 데이터 접근 지점마다 잠금/해제(lock/unlock)를 잊지 않고 수행해야 합니다. 모든 책임이 개발자에게 있습니다.

반면 Ada는 언어 차원에서 동시성(Concurrency)을 지원합니다. Protected 객체는 컴파일러가 잠금/해제를 자동으로 처리해주는 안전한 금고와 같습니다. 개발자는 잠금에 대해 신경 쓸 필요 없이, 정해진 규칙대로 금고 안의 데이터에 접근하기만 하면 됩니다.


## Protected 객체를 이용한 해결 방안

cim.c의 전역 변수들(cim_handle, cim_plugin, cim_ref_count)은 여러 스레드에서 공유되는 위험한 데이터입니다. 이들을 protected 객체 안에 넣어 완벽하게 보호할 수 있습니다.

Ada 구현 예시 (cim_manager.adb)
-- C 인터페이스를 위한 패키지
with Interfaces.C; use Interfaces.C;
with System;

package body CIM_Manager is

   -- 공유되는 모든 전역 변수를 보호할 안전 금고
   protected Plugin_State is
      -- 이 프로시저를 호출하는 작업(스레드)은 자동으로 독점 접근 권한을 얻음
      procedure Increment_And_Load (Success : out Boolean);

      -- 이 프로시저 또한 자동으로 독점 접근 권한을 얻음
      procedure Decrement_And_Unload;

      function Get_VTable return System.Address;
   private
      Ref_Count : Natural := 0;
      Plugin_Handle : System.Address := System.Null_Address;
      Plugin_VTable : System.Address := System.Null_Address;
      -- cim_plugin 포인터 대신 vtable 주소를 바로 저장
   end Plugin_State;

   protected body Plugin_State is
      procedure Increment_And_Load (Success : out Boolean) is
      begin
         Ref_Count := Ref_Count + 1;
         Success := True;

         -- 처음 호출될 때만 플러그인을 로드
         if Ref_Count = 1 then
            -- 여기에 dlopen, dlsym, 버전 체크 등 모든 초기화 로직을 넣습니다.
            -- C의 if (cim_ref_count == 1) 블록 내용이 여기에 들어옵니다.
            -- 만약 로딩에 실패하면 Success := False; 로 설정합니다.
            -- 로딩에 성공하면 Plugin_VTable에 dlsym으로 얻은 주소를 저장합니다.
            null; -- 로딩 로직 (생략)
         end if;

         if not Success then -- 만약 로딩 실패 시
             Ref_Count := Ref_Count - 1; -- 원상 복구
         end if;
      end Increment_And_Load;

      procedure Decrement_And_Unload is
      begin
         Ref_Count := Ref_Count - 1;

         if Ref_Count = 0 then
            -- 여기에 dlclose 등 모든 정리 로직을 넣습니다.
            Plugin_Handle := System.Null_Address;
            Plugin_VTable := System.Null_Address;
         end if;
      end Decrement_And_Unload;
      
      function Get_VTable return System.Address is
      begin
         return Plugin_VTable;
      end Get_VTable;
      
   end Plugin_State;

   -- cim.h의 cim_ic_create 함수와 연결될 C 래퍼
   function C_Create_IC return System.Address is
      Success : Boolean;
   begin
      Plugin_State.Increment_And_Load (Success); -- 안전하게 호출
      
      if Success then
         -- 성공 시, vtable의 create 함수를 호출하는 로직
         -- VTable_Ptr := Plugin_State.Get_VTable;
         -- return VTable_Ptr.Create(...);
         return System.Null_Address; -- 예시
      else
         return System.Null_Address; -- NULL 반환
      end if;
   end C_Create_IC;
   pragma Export (C, C_Create_IC, "cim_ic_create");

end CIM_Manager;

## 이 방식의 장점

항목C (atomic + 수동 처리)Ada (Protected 객체)
안전성개발자가 잠금을 잊으면 바로 버그 발생컴파일러가 잠금을 보장. 실수 원천 차단.
코드 가독성동시성 로직이 코드 전체에 흩어져 있음동시성 로직이 protected body 내에 집중되어 명확함.
신뢰성경쟁 상태, 데드락 등 다양한 버그 발생 가능언어 차원에서 경쟁 상태를 방지. 매우 신뢰성 높음.

## 결론

그렇습니다. Ada의 Protected 객체는 이 문제를 해결하기 위해 태어난 기술이라고 해도 과언이 아닙니다. C로 구현할 때 발생할 수 있는 수많은 동시성 버그를 언어 문법 차원에서 원천적으로 방지해주므로, 훨씬 쉽고 안전하게 견고한 코드를 작성할 수 있습니다. 훌륭한 결정입니다. 👍


Ada 언어는 외계인들이 설계한게 맞지?? ㅎㅎ


ㅎㅎ 그렇게 생각하실 만도 합니다. Ada의 설계 철학을 보면 '인간의 실수를 절대로 용납하지 않겠다'는 외계인 수준의 단호함이 느껴지니까요.

실제로 Ada는 미 국방부(U.S. Department of Defense)가 주도해서 만들었습니다. 1970년대, 국방 시스템에 사용되는 프로그래밍 언어가 수백 가지에 달해 통제가 불가능해지자, 이 모든 것을 대체할 단 하나의 '궁극의 언어'를 만들기로 한 것이죠.


## Ada가 '외계인 언어'처럼 느껴지는 이유

Ada의 설계 목표는 일반적인 상업용 소프트웨어와는 차원이 달랐습니다.

  1. 안전, 안전, 그리고 안전 (Safety First)

    • 목표: 미사일, 전투기, 인공위성 등 실패가 곧 재앙으로 이어지는 시스템을 만들기 위한 언어였습니다.
    • 결과: 컴파일러가 아주 사소한 실수까지도 전부 잡아내도록 설계됐습니다. C라면 그냥 넘어갈 코드도 Ada 컴파일러는 정말 이 코드가 당신의 의도입니까?라고 수십 번 되묻는 것처럼 느껴지죠. protected 객체처럼 동시성 문제를 언어 차원에서 안전하게 만든 것도 같은 맥락입니다.
  2. 가독성을 향한 집착 (Readability Over Writability)

    • 목표: 코드는 한 번 작성되지만, 수십 년간 여러 사람에 의해 읽히고 유지보수되어야 한다고 봤습니다.
    • 결과: if...end if;, loop...end loop; 처럼 구문이 다소 길고 장황해 보이지만, 코드의 시작과 끝이 명확하여 누가 읽어도 오해의 소지가 없도록 만들었습니다. C의 모호한 {}와는 철학이 다릅니다.
  3. 미래를 내다본 설계

    • 목표: 1970년대에 이미 멀티코어와 분산 시스템이 일반화될 미래를 내다보고 언어를 설계했습니다.
    • 결과: 동시성(task, protected object), 패키지를 통한 모듈화, 제네릭 등 당시로서는 혁신적인 개념들을 언어의 핵심 기능으로 내장했습니다. 다른 언어들이 라이브러리로 겨우 지원하던 기능들을 Ada는 이미 품고 있었던 셈이죠.

그래서 Ada는 외계인이 만든 것은 아니지만, 인류가 만들 수 있는 가장 체계적이고 엄격한 방법론을 총동원하여 탄생한 언어라고 할 수 있습니다. 👽 C처럼 자유분방한 언어에 익숙한 개발자에게는 마치 미래에서 온 것처럼 느껴지는 것이 당연합니다.


https://nimfsoft.art/ko

추천 비추천

0

고정닉 0

0

댓글 영역

전체 댓글 0
본문 보기

하단 갤러리 리스트 영역

왼쪽 컨텐츠 영역

갤러리 리스트 영역

갤러리 리스트
번호 제목 글쓴이 작성일 조회 추천
설문 센터해도 될 것 같이 비주얼 물오른 아이돌 멤버는? 운영자 25/09/22 - -
이슈 ‘용원게이’를 넘어 배우 장용원으로 운영자 25/09/23 - -
AD 프로게이머가 될테야!! 운영자 25/09/12 - -
공지 프로그래밍 갤러리 이용 안내 [96] 운영자 20.09.28 47437 65
2891887 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 17:20 7 0
2891886 AI로 PPT제작하는데 불편 ㅇㅇ갤로그로 이동합니다. 17:19 12 0
2891885 근데 언어에 왜 목숨 걸지 [2] 루도그담당(118.235) 17:11 34 0
2891884 섹스지향적 프로그래밍은 없냐 [1] 루도그담당(118.235) 17:08 16 0
2891883 조선식 교육 받으면서 사용법만 알다가 [2] ㅆㅇㅆ찡갤로그로 이동합니다. 17:00 25 0
2891882 에일리언 ♥냥덩이♥갤로그로 이동합니다. 17:00 9 0
2891880 아무리 생각해도 프로그래밍에 재능이 없다 [2] ㅆㅇㅆ찡갤로그로 이동합니다. 16:57 23 0
2891879 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 16:52 9 0
2891878 너네 파워쉘 자주 사용하는 명령어 다시 치는거 귀찮지 않냐? 프갤러(112.144) 16:50 15 0
2891877 LSAT 영어 지문 무딱갤로그로 이동합니다. 16:39 11 0
2891875 요새 디시인사이드 사람이 검열함?? [3] 나르시갤로그로 이동합니다. 16:32 25 2
2891874 홍정모 포프 섹스톤 등 유명개발자들 나이가 영포티누 이기이기 타이밍뒷.통수한방(1.213) 16:28 15 0
2891873 흐미.. 주가 떨어지는 소리가 들리네.. 허걱 나르시갤로그로 이동합니다. 16:16 17 0
2891872 흠.. ♥냥덩이♥갤로그로 이동합니다. 16:15 14 0
2891871 공적으로 사람 만났을 때 AI처럼 또박또박 말하면 [1] 나르시갤로그로 이동합니다. 16:13 19 0
2891870 코테 봐서 똑똑한 애들 뽑아서 만드는게 카톡? ㅇㅇ(223.39) 16:06 19 0
2891868 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 15:31 15 0
2891867 asp 도움되냐? [1] 프갤러(210.93) 15:23 16 0
2891866 오늘 외부 업무는 끝남.. ㅋㅋ 나르시갤로그로 이동합니다. 15:22 12 0
2891864 누가 더 유명함 메시 비욘세 디바(211.235) 14:40 24 0
2891863 ai랑 대화 안하고 프갤 와서 설치는 이유 [1] 프갤러(218.154) 14:38 37 2
2891862 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 14:37 21 0
2891860 안녕하세요 [1] 아스카영원히사랑해갤로그로 이동합니다. 14:30 23 0
2891858 크라우드펀딩애들은 수수료 왤케 많이먹음 공기역학갤로그로 이동합니다. 14:07 17 0
2891856 극좌틀딱톡 쓰기 싫당 ♥냥덩이♥갤로그로 이동합니다. 13:56 17 0
2891855 9월말 끝내고 동시에 3ㅌㅊ 들어갈듯? ♥냥덩이♥갤로그로 이동합니다. 13:53 28 0
2891854 [대한민국] 트럼프 UN 연설에 대한 생각 프갤러(121.172) 13:46 16 0
2891852 경제도 못하고 외교도 못하는 국제왕따 리재명 ㅠ ♥냥덩이♥갤로그로 이동합니다. 13:33 21 0
2891851 나는 나르시 왜 저래 ai도배하는지 모르겠음.. [1] ㅆㅇㅆ찡갤로그로 이동합니다. 13:31 37 0
2891850 와! 오퍼레터 왔다! [1] 프갤러(118.216) 13:04 28 0
2891849 오늘 시간 넘 많아 개심심하네 ㅋㅋ 나르시갤로그로 이동합니다. 13:03 17 0
2891847 바이브코딩보면 웹개발이 얼마나 날먹인지 탄로난거임 ㅋ ㅋ [2] 프갤러(218.148) 12:56 42 0
2891846 혜경이 어디감? 국민혈세낭비 논란 ♥냥덩이♥갤로그로 이동합니다. 12:56 17 0
2891845 자살이 암을 제꼈다는구나 [8] 헬마스터갤로그로 이동합니다. 12:52 50 0
2891844 외교천재리짜이밍 텅텅콘 국격박살나는현실 ㅋㅅㅋ ♥냥덩이♥갤로그로 이동합니다. 12:47 31 0
2891843 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 12:46 28 0
2891842 공부가 진짜 엉덩이 싸움인거같음ㄹㅇ... 아직 재능의 영역 모르겠음 [1] ㅇㅇ(223.39) 12:46 33 0
2891841 요즘 프갤 보다보면 궁금해지는거 [4] ㅇㅇ갤로그로 이동합니다. 12:44 46 0
2891839 모바일크롬 주소창 아래로 바겻노 [7] 헬마스터갤로그로 이동합니다. 12:22 32 0
2891837 210.217 보다 내가 더 뛰어남 병신들아 ㅋㅋ [2] ㅇㅇ(118.235) 12:17 28 2
2891836 그누보드6는 업그레이드된거임 다운그레이드임?? 타이밍뒷.통수한방(1.213) 11:56 16 0
2891835 내가 비정상임? ㅇㅇ(211.234) 11:52 25 0
2891834 요즘 서연고는 저능아만 가는 대학아님? ㅇㅇ(125.141) 11:50 31 1
2891833 재명이 때문에 통화스와프도 못해서 환율 박살남 ♥냥덩이♥갤로그로 이동합니다. 11:49 16 0
cim.c 코드를 평가하라. 나르시갤로그로 이동합니다. 11:19 37 0
2891831 나르시시스트 할배의 오늘 다음 일정 ㅋㅋ 나르시갤로그로 이동합니다. 11:13 16 0
2891830 러빨러, 뿔빨러, ㅆㅇㅆ 등은 왜 코드 안 올리냐 ???? 나르시갤로그로 이동합니다. 11:10 26 0
2891829 이재명 때문에 환율 1400 돌파 IMF위기 공포 도래 [4] ♥냥덩이♥갤로그로 이동합니다. 11:10 42 0
2891828 보수는 현실을 살고 극좌는 망상속에 산당 By 나님 ♥냥덩이♥갤로그로 이동합니다. 11:09 27 0
뉴스 [뚜벅이 맛총사] '평균 키 185cm' 권율x연우진x이정신, 의도치 않은 '뒷골목 형님 바이브'? "무서워..." 후덜덜! 디시트렌드 14:00
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

뉴스

디시미디어

디시이슈

1/2