디시인사이드 갤러리

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

갤러리 본문 영역

cim.c 코드를 평가하라.

나르시갤로그로 이동합니다. 2025.09.25 11:19:41
조회 113 추천 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/10/06 - -
AD 프로게이머가 될테야!! 운영자 25/10/01 - -
2892248 내가 느끼는게 미래는 보지같음 [2] ㅆㅇㅆ찡갤로그로 이동합니다. 09.26 105 0
2892247 아무튼 러스트 베비로 게임 만들면 정상화 될거임 프갤러(110.8) 09.26 56 0
2892245 요즘 게임이 게임 안하는 사람 손에 쥐락펴락 되고 있긴 해 프갤러(110.8) 09.26 64 0
2892244 적당한 그래픽에 [1] 루도그담당(58.239) 09.26 80 0
2892243 최적화는 고사하고 버그도 똑바로 못고치는 프갤러(110.8) 09.26 67 0
2892242 여기 윈도우충들 램 몇기가씀? 난 16기가인데 [2] ㅇㅇ갤로그로 이동합니다. 09.26 70 0
2892240 음 역시 자바가 좀 업계를 망쳐놓은 감이 있지. [1] 프갤러(110.8) 09.26 84 0
2892239 그래픽은 신라면 매운맛 같은거임. ㅆㅇㅆ(124.216) 09.26 53 0
2892237 당장 옛날 게임 그래픽식으로 내면 살 새끼가 적어 ㅆㅇㅆ(124.216) 09.26 60 0
2892235 요즘 게임 최적화 문란한거 같긴 해 프갤러(110.8) 09.26 51 0
2892234 자꾸 저수준 최적화랑 현대 최적화 기법이 다른걸 이해를 못하노.. ㅆㅇㅆ(124.216) 09.26 67 0
2892224 자다 일났다 루도그담당(58.239) 09.26 50 0
2892223 잠을너무몬잣어 ㅇㅅㅇ [6] 따당갤로그로 이동합니다. 09.26 112 0
2892218 패키지게임 시장, 복돌로 개박살내고서 부분유료화 유행시킨게 조선인인데 ㅇㅇ(106.241) 09.26 62 1
2892216 옛날 마리오 용량 작은거 가지고와서 최적화 운운하는거보면 어이가 없는데 [16] ㅇㅇ(106.241) 09.26 200 0
2892211 비전공 IT 국비는 어떻게 취업 어떻게하냐? [3] 프갤러(118.216) 09.26 215 0
2892210 상황이 수 없이 많이 생기는데 그런거 어떻게 다 통제를 하지 [2] ㅇㅇ(106.241) 09.26 63 0
2892209 노동해방이여 오라 [1] ㅇㅇ(39.7) 09.26 54 0
2892208 진지하게 지금이 카카오톡의 아성을 넘을 메신저가 나올 기회 아니냐 [7] 프갤러(110.8) 09.26 118 0
2892207 어느 순간을 기점으로 '데몬'이라는 말 잘 안씀 [3] 박민준갤로그로 이동합니다. 09.26 73 0
2892206 나님 발상의전환? ♥냥덩이♥갤로그로 이동합니다. 09.26 62 0
2892204 F ㅇㅇ(125.182) 09.26 43 0
2892203 난 류도그 저렇게 다중이로 저러는거 이해가 안감..역시 씹도그킹담갓찐 [2] ㅆㅇㅆ(124.216) 09.26 84 0
2892201 늦잠 출근은 할짓이 못되네 루도그담당(58.239) 09.26 52 0
2892200 유명재한테 사기당하고 프갤러(210.98) 09.26 48 0
2892199 여긴 왜 야갤 2중대 됐냐 [2] ㅇㅇ갤로그로 이동합니다. 09.26 83 0
2892198 피곤하다 피곤해 [2] 루도그담당(58.239) 09.26 65 0
2892197 누나가 말끝에 알겠지? 하고 한번 더 확인하면 꼴리지 않냐? 프갤러(223.63) 09.26 67 0
2892196 우리 아들 장애인입니다 [1] 210.217엄마(118.235) 09.26 86 2
2892195 먹고 살기 오늘도 힘들군 [2] ㅆㅇㅆ(124.216) 09.26 85 0
2892194 아힘들다 프갤러(210.217) 09.26 62 0
2892193 최종결전까지 6ㅌㅊ !! ♥냥덩이♥갤로그로 이동합니다. 09.26 64 0
2892192 영포티는 남자만을 지칭하는게 아님 ♥냥덩이♥갤로그로 이동합니다. 09.26 52 0
2892191 애널의달성 4/// ♥냥덩이♥갤로그로 이동합니다. 09.26 71 0
2892190 찢은 보수진보 대통합을 이루고 국가붕괴시킴 ㅋㅋㅋㅋㅋㅋ 타이밍뒷.통수한방(1.213) 09.26 42 0
2892189 오늘 불금이다 [1] 루도그담당(118.235) 09.26 82 0
2892188 파일 이름 길게하니까 ㅇㅇ(106.241) 09.26 71 0
2892187 나님 기술보증 ♥냥덩이♥갤로그로 이동합니다. 09.26 68 0
2892186 Cpu와 gpu의 공통점은 루도그담당(118.235) 09.26 69 0
2892185 GPU가 뭐하는 애인지 모르겠어 [3] ㅇㅇ(106.241) 09.26 100 0
2892184 ㅇㅇ 탕탕(106.101) 09.26 52 0
2892183 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 09.26 76 0
2892182 존나 피곤하다 진짜 ㅆㅇㅆ(124.216) 09.26 67 0
2892181 Outlier에서 지금 까지 번 수입 프갤러(106.101) 09.26 76 0
2892180 한국 미래 예측 [3] ㅇㅇ(175.208) 09.26 82 0
2892179 좇센왕들은 흰머리가 하나도 없누 ㅋㅋㅋㅋㅋㅋㅋ개좇센역사 ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 타이밍뒷통수한방(1.213) 09.26 62 0
2892178 동작그만, 넥도리아(223.38) 09.26 91 0
2892177 레전드 이런데가 있나... 넥도리아(223.38) 09.26 73 0
2892176 귀멸 실사화 퀄리티 ㄷㅅㄷ ♥냥덩이♥갤로그로 이동합니다. 09.26 68 0
2892175 남의 엄마 그만 찾고 러스트나 배우십시오 [1] 프갤러(218.154) 09.26 81 1
뉴스 “스태프가 ‘XX 싸가지 없는 애’라고” 자폭한 남자연예인 디시트렌드 10.08
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

뉴스

디시미디어

디시이슈

1/2