http proxy 개발 문의드립니다.

kwonsu의 이미지

안녕하세요.

다름이 아니라 간단한 http proxy 서버를 만들어볼려고 합니다.

proxy의 기능을 단순히 생각하여 client에서 받은것을 remote 서버에 그대로 넘겨주는 것으로 했습니다.

소켓열기
 
len = recv ( client_sockfd, buff, 2048, 0 );      // 클라이언트에서 데이터 받기
remote_sockfd = webj_connect( hostname );         // remote에 연결할 소켓 열기
ret = send ( remote_sockfd, buff, len, 0 );       // remote에 데이터 보내기
 
len = recv ( remote_sockfd, real_buff, 2048, 0 ); // remote에서 데이터 받기
send ( client_sockfd, real_buff, len, 0);         // 클라이언트에 데이터 보내기
 
while ( ( len = recv ( remote_sockfd, real_buff, 2048, 0 ) ) > 0 ) { // remote의 남은 데이터 받기
     send ( client_sockfd, real_buff, len, 0);   // 클라이언트에 데이터 보내기
     memset( real_buff, 0x00, 2048 );
}

데이터 흐름은 이렇게 생각하고 테스트용으로 만들어봤습니다.

그런데 일반 웹접속와는 차이가 있었으며 그것은 플레쉬, 이미지 파일등을 가져오지 못한것이 주된 것이였습니다.

링크를 누르면 물론 페이지를 가져오지 못했구요.

제가 생각한 방법이 틀린건지요.

다른 방법이 있다면 어떤 방법이 있는지 궁금합니다.

익명 사용자의 이미지

프록시 사용시의 헤더가 평상시의 헤더와 틀린 것으로 알고 있습니다.
프록시 관련 rfc문서를 확인하는게 제일 빠를 듯 합니다.

기본적으론 아마 이렇게 되겠죠.

/code
브라우저 -> 프록시 -> 웹서버
------------ --------------------- -----------
프록시헤더 프록시헤더를 떼고 request수신
송신 일반 http request송신

웹서버 -> 프록시 -> 브라우저
------------ --------------------- -----------
컨텐츠송신 컨텐츠에 프록시 헤더 컨텐츠표시
추가

code/

아니면.... ㅈㅅ ㅠ.ㅜ

declspec의 이미지

웹브라우저에서 프록시 설정을 할 경우 HTTP 헤더가 달라집니다

자기실력이 좋다고 느껴지는건 공부를 안하고 있다는 신호.

익명 사용자의 이미지

지금 쓰신 방법은 tp 모드인 경우입니다.

squid나 vanish 참고

익명 사용자의 이미지

좀더 생각하고 해봐야겠군요.

달아주신 답글이 큰 도움이 되었습니다.

답글 주신 모든분들 감사합니다.

익명 사용자의 이미지

답글이 예의가 발라서 다시 첨언하자면,

저렇게 릴레이를 해줄려면 IP까지 스푸핑 해야됩니다.

그게 아니라면, 새로 접속하는 세션에서는 CONNECT .. 로 시작하는 프록시 전용 프로토콜을 써야겠죠

이 부분은 위에 다른 분이 설명해줬고,

ip 까지 스푸핑해서 투명하게 프록싱 하는거라면 위 방식대로 하면 됩니다.

다만 좀더 세부사항은 깊게 들어가야겠죠........

kwonsu의 이미지

squid 같은 프로그램은 분석하지 못했습니다.

대신 인터넷에서 떠도는 simple proxy 소스코드를 참고삼아 테스트로 만들어보았는데 헤더분석, ip 변경 기타등등 아무것도

하지 않고 위에서 말한 것처럼 client의 웹브라우저에서 보낸 http 헤더와 데이터를 받아 외부 서버에 보내고 이에 대한 결과를

받아 다시 client에 보내는 것만 했습니다. 그런데 외부 웹서버에서 recv로 잘 받지 못하는것 같습니다.

코딩순서는 이렇습니다.

1. proxy 기능을 하는 스레드 생성
2. client에서 받는 소켓 열기
3. accept로 대기
4. 데이터를 받으면 client와 외부서버간 통신하는 쓰레드 생성
5. client, 외부 서버 통신시작
6. 쓰레드 종료

이렇게 되있습니다.

소스코드의 일부를 올리자면

아래는 client에서 데이터를 받기위한 accept 처리 부분입니다.

참고로 소스코드가 지저분합니다. 넒은 이해부탁드립니다.

void *webj_proxy( void *data )
{
 
  int i = 0;
  int ret = 0;
  int opt = 1;
  int state = 0;
  int client_len = 0;
  int client_sockfd = 0;
  int recv_size = 873800;
  int pth_id = 0;
  int optval = 0;
  int optlen = 0;
  struct sockaddr_in  server_addr, client_addr, my_addr;
  struct linger ling;
 
  //pthread_detach(pthread_self());
 
  server_sockfd = socket( AF_INET, SOCK_STREAM, 0 );
  if ( server_sockfd < 0 ) {
    return NULL;
  }
 
  //setsockopt( server_sockfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt) );
 
  setsockopt( server_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt) );
 
// optlen = sizeof(optval);
// getsockopt( server_sockfd, SOL_SOCKET, SO_RCVBUF, (void *)&optval, &optlen );
// printf("before optval = %d ",optval);
//
// ling.l_onoff = 1;
// ling.l_linger = 0;
// setsockopt( server_sockfd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
 
  bzero( &server_addr, sizeof(server_addr) );
 
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(8080);
 
  state = bind( server_sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr) );
  if ( state < 0 ) {
    close(server_sockfd);
    return NULL;
    //return -1;
  }
 
  state = listen( server_sockfd, 1);
  if ( state < 0 ) {
    return NULL;
  }
 
  for ( i = 0 ; i < 100 ; i++ ) {
    c_data[i].pth_pid = -1;
    c_data[i].c_sfd = -1;
  }
 
  while ( 1 ) {
    client_sockfd = accept( server_sockfd, (struct sockaddr *)&client_addr, (socklen_t *)&client_len );
 
    getpeername(client_sockfd, (struct sockaddr *)&my_addr, (socklen_t *)&client_len);
 
    printf("client port is %d\n",my_addr.sin_port);
    printf("client ip is %s\n",inet_ntoa(my_addr.sin_addr));
    printf("client sockfd:%d\n",client_sockfd );
 
    for ( i = 0 ; i < 100 ; i++ ) {
      if ( c_data[i].pth_pid == -1 ) break;
    }
 
    if ( i >= 100 ) {
      printf("pthread error!!!\n");
      close( client_sockfd );
      continue;
    }
 
    c_data[i].c_sfd = client_sockfd;
    pth_id = pthread_create( &c_data[i].pth_pid, NULL, webj_remote, (void *)&i );
  }
 
  return NULL;
 
}

아래는 client 와 외부 웹서버와의 통신 부분입니다.

void *webj_remote( void *data )
{
 
  int pth_num = 0;
  int ret = 0;
  int len = 0;
  int contentlength = 0;
  int remote_sockfd = 0;
  char *szHeaderValue = NULL;
  char buff[1024];
  char recv_buff[8192];
  char tmp_buff[8192];
  char hostname[256];
  unsigned long ip;
 
  pthread_detach (pthread_self());
 
  pth_num = *((int *)data);
 
  memset( buff, 0x00, 1024 );
  memset( recv_buff, 0x00, 8192 );
  memset( tmp_buff, 0x00, 8192 );
  memset( hostname, 0x00, 256 );
  contentlength = 0;
 
  len = recv ( c_data[pth_num].c_sfd, recv_buff, 8192, 0 );
  recv_buff[strlen(recv_buff)] = '\0';
 
  strncpy( tmp_buff, recv_buff, sizeof(recv_buff) );
 
  remote_sockfd = webj_connect( hostname );
 
  if ( remote_sockfd == -1 ) {
    close( remote_sockfd );
    close( c_data[pth_num].c_sfd );
 
    pthread_mutex_lock(&mutex_lock);
    c_data[pth_num].pth_pid = -1;
    c_data[pth_num].c_sfd = -1;
    pthread_cond_signal(&thread_cond);
    pthread_mutex_unlock(&mutex_lock);
    return NULL;
  }
 
  ret = send ( remote_sockfd, recv_buff, len, 0 );
 
  memset( recv_buff, 0x00, 8192 );
  len = recv ( remote_sockfd, recv_buff, 8192, 0 );
  recv_buff[strlen(recv_buff)] = '\0';
  send ( c_data[pth_num].c_sfd, recv_buff, len, 0);
 
  webj_recvandsnd( remote_sockfd, c_data[pth_num].c_sfd );
 
  //memset( recv_buff, 0x00, 8192 );
  //while ( ( len = recv ( remote_sockfd, recv_buff, 8192, 0 ) ) > 0 ) {
  //
  //    buff[strlen(recv_buff)] = '\0';
  //    send ( c_data[pth_num].c_sfd, recv_buff, len, 0);
  //    //printf("22222222222222222recv len:%d\n", len );
  //    //printf("-------------------------------------------------------\n");
  //    //printf("%s\n", buff );
  //    //printf("-------------------------------------------------------\n");
  //    memset( recv_buff, 0x00, 8192 );
  //
  //}
 
  close( remote_sockfd );
  close( c_data[pth_num].c_sfd );
 
  pthread_mutex_lock(&mutex_lock);
  c_data[pth_num].pth_pid = -1;
  c_data[pth_num].c_sfd = -1;
  pthread_cond_signal(&thread_cond);
  pthread_mutex_unlock(&mutex_lock);
 
 
  return 0;
 
}

외부 웹서버 접속 부부입니다.

int webj_connect( char *hostname )
{
 
  int remote_sockfd = 0;
  int ret = 0;
  int opt = 1;
  int real_len = 0;
  char *ip = NULL;
  char ip_buff[4096];
  struct sockaddr_in  real_addr;
  struct hostent     *myent;
  struct linger ling;
 
  remote_sockfd = socket( AF_INET, SOCK_STREAM, 0 );
  if ( remote_sockfd < 0 ) {
      printf("1. client error:%s\n", strerror(errno) );
      return -1;
  }
 
  memset( ip_buff, 0x00, 4096 );
  get_ip( hostname, ip_buff, sizeof(ip_buff), &myent );
  if ( myent == NULL ) return -1;
 
  remote_sockfd = socket( AF_INET, SOCK_STREAM, 0 );
  real_addr.sin_family = AF_INET;
  memcpy ( &real_addr.sin_addr.s_addr, myent->h_addr, myent->h_length );
  printf("ipaddr:%s\n", inet_ntoa(real_addr.sin_addr) );
  //real_addr.sin_addr.s_addr = inet_ntoa( *(struct in_addr*)myent->h_addr_list[0]);
  //real_addr.sin_addr.s_addr = inet_addr("110.14.221.235");
  real_addr.sin_port = htons(80);
 
  //setsockopt( remote_sockfd, IPPROTO_TCP, TCP_NODELAY, &opt, sizeof(opt) );
 
  //optlen = sizeof(optval);
  //getsockopt( server_sockfd, SOL_SOCKET, SO_RCVBUF, (void *)&optval, &optlen );
  //printf("before optval = %d ",optval);
 
  real_len = sizeof(real_addr);
 
  ret = connect( remote_sockfd, (struct sockaddr *)&real_addr, real_len );
  if ( ret < 0 ) {
      printf("2. client error:%s\n", strerror(errno) );
      close(remote_sockfd);
      return -1;
  }
 
  return remote_sockfd;
}

외부 웹 서버에서 데이터를 가져와서 client에 보내는 부분입니다.

int webj_recvandsnd( int recv_sfd, int send_sfd )
{
  int ret = 0;
  int recv_len = 0;
  int tmp_sock = 0;
  char buff[2048];
  struct timeval timeout;
  fd_set read_set;
 
  memset( buff, 0x00, 2048 );
 
  while ( 1 ) {
 
    FD_ZERO( &read_set );
    FD_SET( recv_sfd, &read_set );
 
    timeout.tv_sec = 3;
    timeout.tv_usec = 0;
 
    tmp_sock = recv_sfd;
 
    ret = select( tmp_sock + 1, &read_set, NULL, NULL, &timeout );
    if ( ret < 0 ) return -1;
 
    if ( FD_ISSET( tmp_sock, &read_set ) ) {
        recv_len = recv( tmp_sock, buff, 2048, 0 );
        if ( recv_len > 0 ) {
          send( send_sfd, buff, recv_len, 0 );
          memset( buff, 0x00, 2048 );
          recv_len = 0;
        }
        else return 0;
    }
    else return 0;
 
  }
 
  return 0;
}

제 질문의 요지는 상당히 단순한 코드인데 왜 접속이 원할하지 못하는걸까 입니다.

좀더 심플하고 가볍게 해서 테스트하려고 했는데 잘 안되는군요.

제가 혹시 놓친 부분이 있는지, 아니면 제가 생각하는 이런 구상이 틀린건지 궁금합니다.

여러분들의 소중한 답변 부탁드립니다.

감사합니다.

지식의 여인은 옷을 쉽게 벗지 않는다.
잡초인생. 잡초처럼 끈길기게....

ymir의 이미지

전체 로직을 훑어본 건 아니지만, 일단 눈에 띈 거 하나만 말씀드리자면..
웹 트래픽이 string 만 있는건 아니죠..
recv_buff[strlen(recv_buff)] = '\0'; 와 같은 루틴은 사족으로 보이고..
더불어 str* 관련 함수들이.. 문제를 일으킬 겁니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

익명 사용자의 이미지

javascript 를 사용하실줄 아신다면 https://github.com/nodejitsu/node-http-proxy 이 프로젝트를 한 번 참고해 보세요
최근에 회사내 프로젝트를 nodejs 를 사용해서 진행했었는데 생각보다 쉽습니다.

kwonsu의 이미지

우선 답변 주셔서 감사합니다. 자바스크립트 알려주셔서 감사합니다. ^^

ymir님의 답변을 듣고 str* 함수들은 교채하거나 뺐습니다. 그래도 속도가 느리더군요.

해서 select 함수에서 사용하는 timeout 부분의 시간을 줄이고 해보니 좀 나아보였습니다.

http 헤더나 본문이 스크링만 오는건 아닌걸로 알고 있는데 client나 외부 웹 서버에서 데이터를

받아오거나( recv ), 보낼때 ( send ) void *buf를 사용하는걸로 아는데 그럼 이부분을 뭘로 받고 보내야 할까요?

텍스트와 바이너를 구분하여 보내야한다는건지 궁금합니다.

제가 말씀드린것처럼 헤더는 전혀 건들지 않고 중계자 역할만 테스트하고 있습니다. ^^

다시한번 답변주셔서 감사합니다. ^^

지식의 여인은 옷을 쉽게 벗지 않는다.
잡초인생. 잡초처럼 끈길기게....

ymir의 이미지

코드를 다시 보니 recv_buff[strlen(recv_buff)] = '\0'; 코드는 사족은 맞지만..
적어도 위 루틴에서는 아무런 영향을 미치지 않는군요.
원래 '\0' 에 다시 '\0' 을 덮어쓰고, 받은만큼 전송을 시도하고 있었네요.

char * 라고 해서, binary data 를 저장할 수 없는 것도 아니고..
또 무조건 str* function family 를 쓸 수 있는 것도 아닙니다.
char * 에 string 만 저장하기로 정한 경우에만 그렇게 쓰시면 됩니다.

실제 프락시쪽은 (잘은 모르겠습니다만) 어쨌든 별도의 헤더들이 붙어야 하는 걸로 알고 있습니다.
위와 같이 단순 소켓 릴레이만 하는 경우는, transparent 한 환경 또는 g/w 상에서 동작해야 할 것으로 보입니다.
어떤 식으로 테스트 했고, 어떤 결과로 인해 오류라고 판단을 했는지 좀 더 구체적으로 서술해 보시면 어떨까 합니다.

프락시의 client 쪽과 server 쪽에서 tcpdump 나 wireshark 로 패킷을 떠서..
http 로 디코딩해 보면서 양쪽의 데이터 송수신에는 문제는 없는지 확인을 해보는 것도 좋을 듯 싶습니다.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

kwonsu의 이미지

일단 감사드립니다.

제가 조사한 바로는 web 헤더와 body는 구분되서 전송이 되고 웹접속해서 테스트할때 그림파일이나 또는 파일 전송에는

문제는 없으나 데몬이 종료된다든지 아니면 웹 페이지 로딩 속도가 현격히 떨어지거나 그림파일들중 몇개정도가 누락된것을

확인하였습니다. 제가 테스한 방법은 크게 두가지로 했습니다.

1. 아주 간단한 웹페이지를 제작하여 테스트. ( 웹브라우저의 proxy 무설정 )
client-------------->proxy server--------------->test web server

웹서버에 있는 웹코딩은 파일전송과 DB 입력을 하도록 했습니다.
테스트는 좀더 해야겠지만 파일전송 및 DB입력은 이상이 없었던것으로 확인했습니다.

2. 웹브라우저의 proxy 설정을 통한 proxy server 테스트
client( proxy 설정 )---------->proxy server----------->외부 web server 접속
proxy server ip 로 설정

외부 web server는 www.daum.net www.naver.com www.empas.com 등으로 접속하여 테스트했습니다.

문제는 여기서 발생했는데 외부 웹 서버를 접속하여 데이터르 주고받을때 외부 웹서버에서 응답이 느리다는 것이였습니다.

물론 장비가 좋은것은 아니였으나 client pc 한대로 접속하여 테스트를 하는데도 속도가 느리다는것이 이상하였습니다.

그리고 속도를 증가시키는 방법이 없나 이것저것 테스트를 해봤을때 non-blocking을 위하여 select를 이용했는데

send 부분에서 데몬이 종료되는것을 확인했습니다. 왜 종료되는지는 아직 확인하지 못했습니다.

proxy server 에서 tcpdump, client pc에서 wireshark으로 패킷을 떠봤는데 client에서는 잘 나가지만

proxy server에서 tcpdump에서 패킷을 떴을때 client에서 받은것이 외부서버로 가는것을 확인하였으나 받는것은

좀처럼 좋아지지 않았습니다.

참고로 www.empas.com으로 접속할때는 페이지가 열리지 않은것을 확인하였습니다. 이유는 아직 파악하지 못했습니다.

어떻하면 해결할수 있는지 참 궁금합니다.

도움주셔서 감사합니다.

지식의 여인은 옷을 쉽게 벗지 않는다.
잡초인생. 잡초처럼 끈길기게....

익명 사용자의 이미지

클라가 proxy 무 설정이면, 투명한 환경을 구축했다고 보입니다.

그럼 외부 웹서버에서 클라로 가는 경로는 1개 뿐이어야 합니다. 즉 무조건 프록시를 거쳐서 가야 투명프록시가 작동합니다.

안그러면 비대칭 라우팅되어 연결되지 않습니다.

그리고 프록시를 설정했다면, 그대로 라우팅해서 웹서버로 접속하면 안됩니다.

프록시 설정하면 GET ... 으로 시작하지 않고 CONNECT.. 이런 헤더로 시작해서 적절한 변환이 필요합니다.

그러지 말고 문서좀 읽어보시지 그래요?...

ymir의 이미지

header 조작 없이 데이터를 전달하는 경우는, transparent proxy 나 g/w 형태로 사용하는 경우입니다.
2번과 같이 브라우저에서 proxy 설정이 들어간 경우는, 처음 두 분이 언급하신대로..
http 헤더가 달라지기 때문에, proxy server 에서 별도로 http header 를 손질해 주어야 할 것 같습니다.
그대로 web server 와 client 로 중계했을 때 어떤 일이 일어날지는 잘 모르겠네요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

익명 사용자의 이미지

kwonsu의 이미지

여러분들의 친절한 답변 감사합니다. 원하던 문서 못찾아 못읽어서 죄송스럽게 생각합니다.

좀 더 생각해보고 해보겠습니다.

답변 주신 모든 분들께 진심으로 감사의 말씀 드립니다. kldp 화이팅. ^^

지식의 여인은 옷을 쉽게 벗지 않는다.
잡초인생. 잡초처럼 끈길기게....

ymir의 이미지

client --- g/w --- server

위와 같이 일반적인 네트워크 환경을 가정합니다.
g/w 에 proxy server 를 올리는데, client 에서 server 로 가는 web traffic 을 iptables 로 dnat 를 걸어서..
server 로 가는 패킷의 ip/port 를 g/w 의 proxy 쪽으로 바꿔줍니다.
proxy 에서는, getsockopt 파라미터 중에 original dst addr 를 얻어올 수 있는 옵션이 있는데.. (SO_ORGINAL_DST 였던가..??)
이걸로 원래의 server 주소를 알아내거나, 아예 server 주소를 고정시켜서 연결을 하면 됩니다.

transparent proxy 는 조금 복잡한데..
tproxy 를 지원하는 최신 커널을 쓰거나, tproxy 패치를 붙인 커널이 필요하고, iptables 도 tproxy 패치가 필요합니다.

client --- bridge --- (g/w) --- server

위와 같이 bridge 환경을 만들고, bridge 에 proxy server 를 올립니다.
여기에 ebtables 를 써서, bridge 로 웹 트래픽을 routing 시키고, iptables 로 dnat 를 겁니다.
(kernel document 중에 tproxy 관련 문서에 설정 방법이 있습니다.)
proxy server 에서는 추가적으로 socket option 에 IP_TRANSPARENT 를 넣고, client 의 ip 로 bind 를 한 후에..
server 로 connect 를 해서.. 양쪽 socket 으로 데이터를 중계해주면 됩니다.

위 두개는 전에 썼던 방법인데, 기억에 의존해서 쓰다보니 좀 엉성합니다만 관련된 키워드는 다 언급한 것 같습니다.
둘 다 똑같이 웹 브라우저의 proxy 설정 없이 일반적인 네트워크 환경에서.. 중간에 proxy 를 끼워 넣을 때 쓴 방법입니다.

2번과 같은 proxy 를 만드시려는 거라면, 다른 분들 조언대로 일단 rfc 나 proxy 관련 문서를 먼저 찾아 보시는게 나을 듯 싶네요.

되면 한다! / feel no sorrow, feel no pain, feel no hurt, there's nothing gained.. only love will then remain.. 『 Mizz 』

kwonsu의 이미지

구조적 원리 설명 감사합니다. 나머지는 삽질로 해보겠습니다.

문서는 찾아 읽어보는편인데 영어가 짦아서 버벅거립니다. ^^

문서나 기타 관련 자료 잘 찾아보고 해보겠습니다.

다시한번 친절하고 이해빠른 답변 주셔서 감사합니다. ^^

지식의 여인은 옷을 쉽게 벗지 않는다.
잡초인생. 잡초처럼 끈길기게....

익명 사용자의 이미지

댓글 달기

Filtered HTML

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

BBCode

  • 텍스트에 BBCode 태그를 사용할 수 있습니다. URL은 자동으로 링크 됩니다.
  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param>
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.

Textile

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • You can use Textile markup to format text.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Markdown

  • 다음 태그를 이용하여 소스 코드 구문 강조를 할 수 있습니다: <code>, <blockcode>, <apache>, <applescript>, <autoconf>, <awk>, <bash>, <c>, <cpp>, <css>, <diff>, <drupal5>, <drupal6>, <gdb>, <html>, <html5>, <java>, <javascript>, <ldif>, <lua>, <make>, <mysql>, <perl>, <perl6>, <php>, <pgsql>, <proftpd>, <python>, <reg>, <spec>, <ruby>. 지원하는 태그 형식: <foo>, [foo].
  • Quick Tips:
    • Two or more spaces at a line's end = Line break
    • Double returns = Paragraph
    • *Single asterisks* or _single underscores_ = Emphasis
    • **Double** or __double__ = Strong
    • This is [a link](http://the.link.example.com "The optional title text")
    For complete details on the Markdown syntax, see the Markdown documentation and Markdown Extra documentation for tables, footnotes, and more.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 사용할 수 있는 HTML 태그: <p><div><span><br><a><em><strong><del><ins><b><i><u><s><pre><code><cite><blockquote><ul><ol><li><dl><dt><dd><table><tr><td><th><thead><tbody><h1><h2><h3><h4><h5><h6><img><embed><object><param><hr>

Plain text

  • HTML 태그를 사용할 수 없습니다.
  • web 주소와/이메일 주소를 클릭할 수 있는 링크로 자동으로 바꿉니다.
  • 줄과 단락은 자동으로 분리됩니다.
댓글 첨부 파일
이 댓글에 이미지나 파일을 업로드 합니다.
파일 크기는 8 MB보다 작아야 합니다.
허용할 파일 형식: txt pdf doc xls gif jpg jpeg mp3 png rar zip.
CAPTCHA
이것은 자동으로 스팸을 올리는 것을 막기 위해서 제공됩니다.