디시인사이드 갤러리

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

갤러리 본문 영역

cim.c 코드를 평가하라.

나르시갤로그로 이동합니다. 2025.09.25 11:19:41
조회 97 추천 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
본문 보기

하단 갤러리 리스트 영역

왼쪽 컨텐츠 영역

갤러리 리스트 영역

갤러리 리스트
번호 제목 글쓴이 작성일 조회 추천
설문 우리나라를 대표해서 UN 연설자로 내보내고 싶은 스타는? 운영자 25/09/29 - -
AD 프로게이머가 될테야!! 운영자 25/10/01 - -
공지 프로그래밍 갤러리 이용 안내 [96] 운영자 20.09.28 47608 65
2893727 나는내향적이야 나는조현병이야 손발이시립디다갤로그로 이동합니다. 12:36 1 0
2893726 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥[CBDC]냥덩코인♥갤로그로 이동합니다. 12:33 3 0
2893725 창업 조언 발명도둑잡기(118.235) 12:23 9 0
2893724 프로그래밍 이야기가 1도 없노 ㅇㅇ(218.157) 12:23 13 0
2893723 신은 왜 쓰레기 같은 새끼한테 시간을 줬을까? 프갤러(210.217) 12:14 14 1
2893722 앱 오버레이 컴포넌트들 다 만들었다 [1] 노력갤로그로 이동합니다. 11:44 20 0
2893721 인공지능, ai 국비하면 취업 가능? [2] cvs.갤로그로 이동합니다. 11:24 32 0
2893720 [이스트소프트] AI 교육받고 제주 자연 속 워케이션 즐기자! [1] [ESTsoft]oreumi-camp(125.183) 10:39 22 0
2893719 뭔지랄을해야 이딴나라에서 태어날수있는거지 [1] 타이밍뒷.통수한방(1.213) 10:35 21 0
2893718 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 10:09 16 0
2893717 ❤✨☀⭐⚡☘⛩나님 시작합니당⛩☘⚡⭐☀✨❤ ♥냥덩이♥갤로그로 이동합니다. 09:33 17 0
2893716 수..숨막혀..ㅇㅅㅇ 헤르 미온느갤로그로 이동합니다. 09:31 17 0
2893714 태연 ㅇㅅㅇ 헤르 미온느갤로그로 이동합니다. 09:26 17 0
2893713 하루 한 번 헤르미온느 찬양 헤르 미온느갤로그로 이동합니다. 09:25 19 0
2893712 의정부역 PC방 ㅇㅇ(223.38) 08:40 12 0
2893711 앱공장 해볼만함? 프갤러(180.231) 08:12 20 0
2893692 넥도리아 양념 헌돈 돼지 갈비 추석을 향해 전진. [1] 넥도리아2025(220.74) 06:22 19 0
2893675 음기 충전 발명도둑잡기(118.216) 05:13 41 0
2893673 조심해라 [1] 프갤러(211.118) 04:44 37 0
2893671 스포티파이 개인정보 국방부로 팔려나가는 건 시간문제 발명도둑잡기(118.216) 04:16 20 0
2893666 이 패션 진짜 올 가을 유행인가요? 발명도둑잡기(118.235) 04:01 23 0
2893665 스포티파이 인공지능 규제 발표 [1] 발명도둑잡기(118.235) 03:56 26 0
2893663 “엡스타인, 정보기관과 연관됐나” 의혹 여전… 美 정부는 침묵 발명도둑잡기(118.235) 03:36 16 0
2893655 흐름 상징 발명도둑잡기(118.235) 02:54 28 0
2893649 한국 국회, 정부, 법원은 여전히 미군위안부같은 정치를 하고 있다 발명도둑잡기(118.216) 02:42 18 0
2893644 사실 세대론으로 비판하면 안되고 생산수단 가진자를 비판해야 발명도둑잡기(118.235) 01:57 24 0
2893642 오늘의 소설, 영화 실마리: 성매매자 데이터베이스가 외국 정보기관에 발명도둑잡기(118.235) 01:52 15 0
2893641 엠스타인 명단 공개 예정인 의원 발명도둑잡기(118.235) 01:38 19 0
2893639 형님들 스타트업은 원래 조용한가요? [4] ㅇㅇ(106.101) 01:16 76 0
2893638 이직 하고 싶은데 물경력이라 방법이 없다.. ㅇㅇ(175.197) 01:16 35 0
2893637 BTS (방탄소년단) 'Dynamite' 발명도둑잡기(118.216) 01:07 20 0
2893636 먹고 살려고 안해본 일이 없는데, IT 프리랜서 장점은 [1] ㅆㅇㅆ(124.216) 01:06 57 0
2893635 내가 느끼는게 랜딩 페이지 하나 작성에 15만 ㅆㅇㅆ찡갤로그로 이동합니다. 00:59 38 0
2893634 프리로 협업 몇번해봤는데 항상 느끼지만 ㅆㅇㅆ찡갤로그로 이동합니다. 00:56 41 0
2893632 어차피 개발하다보면 서로 지식의 빈곳이 있을수밖에 없어서 [3] ㅆㅇㅆ찡갤로그로 이동합니다. 00:48 62 0
2893631 신해철, 싸이-Dear America [1] 발명도둑잡기(118.216) 00:38 32 0
2893629 우리나라가 소프트웨어 강국이 되려면 ㅇㅇ(121.168) 00:30 28 0
2893628 한국 10년뒤엔 러스트충들 국비찍어될듯 타이밍뒷.통수한방(1.213) 00:29 48 0
2893627 일하면 그냥 현타 존나옴 [25] ㅇㅇ갤로그로 이동합니다. 00:24 123 0
2893626 오늘의 작사 실마리: 관세전쟁 [3] 발명도둑잡기(118.216) 00:24 28 0
2893625 진짜 돈 모아서 C드라이브랑 메인보드 교체해야할듯 [2] ㅆㅇㅆ(124.216) 00:20 43 0
2893624 이거 교정 방법 잘못 고른건가? 발명도둑잡기(118.216) 00:18 23 0
2893622 후 아뮤튼 러스트 배워두십시오. 지능이 향상됩니다. [1] 프갤러(110.8) 00:12 39 0
2893620 밀레니얼로서 난 X세대보다 더 행복한 유년기에 꿀빨았다고 생각해 ㅇㅇ(121.168) 00:12 25 0
2893619 간식을 먹는데 입 안에 자꾸 찔려서 눈물이 저절로 난다 발명도둑잡기(118.216) 00:10 20 0
2893618 문화생활 깊이있게 하려면 무조건 일본에 의지 할 수 밖에 없음 [2] ♥냥덩이♥갤로그로 이동합니다. 00:10 64 0
2893616 sm은 원래 현업보다 it팀이 더 업무 프로세스를 잘 앎? [1] 프갤러(182.230) 00:02 37 0
2893615 서울에서 자란 친구들은 공부 못하면 부모가 ADHD의심해? ㅇㅇ(121.168) 00:01 25 0
2893614 이 시절 그리우면 개추 [3] ♥냥덩이♥갤로그로 이동합니다. 00:00 46 1
뉴스 ‘경성대 전지현’ 김연정, 한화 하주석과 결혼…치어리더♥선수 백년가약 디시트렌드 10.02
갤러리 내부 검색
제목+내용게시물 정렬 옵션

오른쪽 컨텐츠 영역

실시간 베스트

1/8

뉴스

디시미디어

디시이슈

1/2