아두이노 관련 멘토를 맡게 되었다.


아두이노를 해본 적이 없는데 맡게 되어서 좀 당황스럽다.


코딩 및 아이디어 도출에 대한 조언을 목적으로 멘토를 뽑았다고 한다.


학생들이 처음 받은 과제는 "서브모터 3개를 활용하여 작품을 만들기" 이다.


서브모터 3개를 활용하여 집게를 만들기로 하였다.


코딩은 다음과 같이 진행을 하였다.


#include <Servo.h>

const int button1Pin = 2;
const int motor1Pin = 9;
const int motor2Pin = 10;
const int motor3Pin = 11;
int motor1moving = true;//false 면 a->b, true면 b->a
int motor2moving = true;
int motor3moving = true;
int buttonPress = false;
Servo motor1;
Servo motor2;
Servo motor3;
bool isRight = false;
int s = 0;
int e = 180;

int motor1angle = 0;
int motor2angle = 0;
int motor3angle = 0;

// 실행시 가장 먼저 호출되는 함수이며, 최초 1회만 실행됩니다.
void setup()
{
  pinMode(button1Pin, INPUT);
  motor1.attach(motor1Pin);
  motor2.attach(motor2Pin);
  motor3.attach(motor3Pin);
  motor1.write(motor1angle);
  motor2.write(motor2angle);
  motor3.write(motor3angle);
}

// setup() 함수가 호출된 이후, loop() 함수가 호출되며,
// 블록 안의 코드를 무한히 반복 실행합니다.
void loop()
{
  int button1State;
  if (buttonPress == false)
  {
    button1State = digitalRead(button1Pin);
    if (button1State == HIGH)
    {
      buttonPress = true;
    }
  }
 if (buttonPress == true)
   {
      if (motor1moving == motor2moving && motor2moving == motor3moving)
      {
        if (motor1moving)
          isRight = true;
        else
          isRight = false;
      }
      if (isRight)
      {
        if (motor1moving == isRight)
        {
          motor1.write(++motor1angle);
	delay(15);
          if (motor1angle == e)
            motor1moving = false;
        }
        else if (isRight == motor2moving)
        {
          motor2.write(++motor2angle);
	delay(15);
          if (motor2angle == e)
            motor2moving = false;
        }
        else if (isRight == motor3moving)
        {
          motor3.write(++motor3angle);
	delay(15);
          if (motor3angle == e)
          {
            buttonPress = false;
            motor3moving = false;
          }
        }
      }
      else
      {
        if (motor1moving == isRight) {
          motor3.write(--motor3angle);
	delay(15);
          if (motor3angle == s)
            motor1moving = true;
        }
        else if (isRight == motor2moving)
        {
          motor2.write(--motor2angle);
	delay(15);
          if (motor2angle == s)
            motor2moving = true;
        }
        else if (isRight == motor3moving)
        {
          motor1.write(--motor1angle);
	delay(15);
          if (motor1angle == s)
          {
            buttonPress = false;
            motor3moving = true;
          }
        }
      }
    }
}

완전한 구조까지 짜줄 수는 없지만 기본 코드로 사용할 수 있도록 다음과 같이 작성하였다.


기능은 다음과 같다.


1. 버튼을 지속적으로 누르는 것이 아닌 1회 버튼 클릭 시 모터가 일정 조건을 만족할 때까지 회전


2. 모터 3개가 1 - 2 - 3의 순서로 순차적으로 회전


3. 순차적으로 회전이 완료된 후 버튼을 재클릭 시, 3 - 2 - 1의 역순으로 회전


집게를 만든다고 할 때, 몸체가 되는 1번 모터가 회전을 하고, 팔꿈치에 해당하는 2번 모터가 회전,


마지막으로 집게에 해당하는 3번 모터가 회전을 한다.


다시 버튼을 눌렀을 때는 집게에 해당하는 3번 모터, 팔꿈치에 해당하는 2번 모터, 몸체에 해당하는 1번 모터가 회전하여


초기 위치로 돌아올 수 있게 한다.


초기 위치에서 물건을 쥐어주고 버튼을 누르면 특정 위치로 이동 후, 물건을 놓은 후에 다시 버튼을 누르면 초기 위치로 돌아온다.


기본 동작 자체는 별 것 없는데 delay()가 필수라던가 하는 부분에서 조금 꼬였다.


#include <Servo.h>

const int button1Pin = 13;
// led를 디지털 13번 핀에 연결합니다.
const int motor1Pin = 9;
const int motor2Pin = 8;
const int motor3Pin = 7;
int motor1moving = true;//false 면 a->b, true면 b->a
int motor2moving = true;
int motor3moving = true;
int buttonPress = false;
Servo motor1;
Servo motor2;
Servo motor3;
bool isRight = false;
int defaultAngle = 0;
int secondAngle = 45;
int thirdAngle = 90;
int maxAngle = 180;

int motor1angle = 0;
int motor2angle = 0;
int motor3angle = 0;

// 실행시 가장 먼저 호출되는 함수이며, 최초 1회만 실행됩니다.
void setup()
{
  pinMode(button1Pin, INPUT);
  motor1.attach(motor1Pin);
  motor2.attach(motor2Pin);
  motor3.attach(motor3Pin);
  motor1.write(motor1angle);
  motor2.write(motor2angle);
  motor3.write(motor3angle);
}

// setup() 함수가 호출된 이후, loop() 함수가 호출되며,
// 블록 안의 코드를 무한히 반복 실행합니다.
void loop()
{
  int button1State;
  if (buttonPress == false)
  {
    button1State = digitalRead(button1Pin);
    if (button1State == HIGH)
    {
      buttonPress = true;
    }
  }
  if (buttonPress == true)
  {
    if (motor1moving == motor2moving && motor2moving == motor3moving)
    {
      if (motor1moving)
        isRight = true;
      else
        isRight = false;
    }
    if (isRight)
    {
      if (motor1moving == isRight) 
      {
        motor1.write(++motor1angle);
        if (motor1angle == maxAngle)//1모터가 180도까지 다 돌았을 경우
          motor1moving = false;//모터 first 완료
          delay(15);
      }
      else if (isRight == motor2moving)
      {
        motor2.write(++motor2angle);
        if (motor2angle == thirdAngle)//1모터가 180도까지 다 돌았을 경우
          motor2moving = false;//모터 first 완료
          delay(15);
      }
      else if (isRight == motor3moving)
      {
        motor3.write(++motor3angle);
        if (motor3angle == secondAngle)//1모터가 180도까지 다 돌았을 경우
        {
          buttonPress = false;
          motor3moving = false;//모터 first 완료
          delay(15);
        }
      }
    }
    else
    {
      if (motor1moving == isRight) {
        motor3.write(--motor3angle);
        if (motor3angle == defaultAngle)//3모터가 0도까지 다 돌았을 경우
          motor1moving = true;//모터 first 완료
          delay(15);
      }
      else if (isRight == motor2moving)
      {
        motor2.write(--motor2angle);
        if (motor2angle == defaultAngle)//1모터가 180도까지 다 돌았을 경우
          motor2moving = true;//모터 first 완료
          delay(15);
      }
      else if (isRight == motor3moving)
      {
        motor1.write(--motor1angle);
        if (motor1angle == defaultAngle)//1모터가 180도까지 다 돌았을 경우
        {
          buttonPress = false;
          motor3moving = true;//모터 first 완료
          delay(15);
        }
      }
    }
  }
}

학생들이 위의 코드를 바탕으로 수정한 내용.


bool 변수 비교가 살짝 문제가 있었는지 수정을 하였고 결과가 괜찮게 나왔다.




지역 이진 패턴을 이용하여 텍스처의 특징을 추출한다.


이전에 해썬 SIFT보다 간단하기 때문에 크게 설명할 부분은 없다.




	Mat image = imread("Lenna.png");//이미지 보관.
	Mat gray;   //grayscale 이미지를 저장할 Mat.
	cvtColor(image, gray, CV_BGR2GRAY);//grayscale 이미지 사용.
	Mat lbp(image.size(), CV_8U, Scalar(0));


CV_8U는 unsigned char => 0~255 사이의 값을 같는 uchar를 나타낸다. Schalar(0) 기본값을 0으로 초기화한다.


lbp에 각 값들을 집어넣은 후에 출력한다.


	for (int i = 1; i < gray.rows - 1; ++i) {
		for (int j = 1; j < gray.cols - 1; ++j)
		{
			uchar t[9];
			for (int k = 0; k < 3; ++k) {
				for (int l = 0; l < 3; ++l) {
					t[k * 3 + l] = gray.at<uchar>(i + k - 1, j + l - 1);
				}
			}
			uchar currentValue = ReturnDecimVal(t);


			lbp.at<uchar>(i, j) = currentValue;
		}
	}


사이드에서 계산을 할 경우, 범위를 벗어난다. padding을 하는 방법도있지만 생략한다.


LBP는 패딩을 해도 별 차이가 없다.


copyMakeBorder(gray, gray, 1, 1, 1, 1, BORDER_CONSTANT, Scalar(0)); 다음과 같이 쓰면 제로 패딩을 해볼 수 있다.


uchar 배열에 자기자신과 둘러싼 8개의 값을 집어넣고 이를 LBP를 이용하여 적절한 값을 찾아낸다.


uchar ReturnDecimVal(uchar* t) {
	uchar val = 0;
	uchar center = t[1 * 3 + 1];
	bool check[9];
	int calStance[8] = { 3, 6, 7, 8, 5, 2, 1, 0 };//계산 순서를 임의로 설정.

	for (int k = 0; k < 3; ++k) {//ture면 주변 색보다 센터가 크거나 같은 경우, false면 아닌 경우.
		for (int l = 0; l < 3; ++l) {
			check[k * 3 + l] = (center >= t[k * 3 + l]) ? true : false;
		}
	}
	for (int i = 7; i >= 0; --i)
		val += check[calStance[7-i]] * pow(2, i);//위의 계산 순서에 따라서 2진수 방식으로 계산. bool은 false = 0, true = 1이므로 그대로 사용.

	return val;
}

위의 달팽이 그림같은 패턴을 그대로 사용하지 않아도 된다고 하지만 위에 맞춰서 패턴을 입력해보았다.


간단하게 하고자 계산 순서를 정해놓고 한바퀴 도는 식으로 했다.


달팽이 알고리즘을 짜고 싶다면 정보처리기능사 문제를 보면 된다.


효율적이라고 생각하진 않는다.





http://game-icons.net/


jsp 관련 문서를 작성하는데 word에서는 자꾸만 'j네' 또는 'J네'로 변경이 된다.


[파일] - [옵션]을 선택한다.



[언어 교정] - [자동 고침 옵션]을 선택한다.



입력 창과 결과 창에 값을 입력하면 된다.


http://Render-Crowd-Of-Animated-Characters-master


애니메이션을 가진 캐릭터의 Mesh를 이용하여 해당 애니메이션에 맞는 Anim Map을 생성해준다.


이를 이용하면 Shader에서 Mesh의 각 Vertex 부분을 이동시킨 것과 같은 모양으로 렌더링을 해준다.


실제로 Mesh의 모양을 변형하는 것이 아니다.


gpu instancing을 사용하여 draw call을 줄여준다고 한다.


써보면 되게 매력적이다.


http://orcacode.tistory.com/entry/Unity-Android-Bluetooth-플러그인과-원리


남는 학점 채우려고 했던 과목이었지만 재밌었다.


아이디어도 중요하지만 섬세함이 많이 필요한 것이 디자인이라는 생각이 많이 들었다.


지금보면 어딘가 부족해보이는 부분은 내가 섬세함이 부족했기 때문이라는 생각이 든다.



주제 : 홍보물 제작


캡콤, SEED9, CJ >> 마계촌 온라인 포스터의 밑부분을 활용한 것 같다. 지금은 망했다.


주변에 둥실둥실 떠다니는 아이콘과 부처 뒤의 나무가 조금 아쉽다


색감 보정에 대한 강의 후에 만든 과제였을 것이다.



주제 : 전자제품 홍보


예전부터 나는 엑스페리아 시리즈가 좋았다. 


아이폰은 4부터 5S까지의 각진 디자인이 좋았고 엑스페리아는 언제나 각진 디자인이라 좋았다.


XZ2가 곡면 디자인을 넣었으니 성능 안좋고 디자인도 못생긴 폰이 되어버렸다.



과제 주제 : 브랜드의 크리스마스 이미지 만들기


안드로이드 마스코트는 다양한 이미지가 있어서 사용하기 좋다.



주제 : 스마트폰 특정 브랜드의 위젯, 인터페이스 및 잠금화면 제작.


iOS는 해볼 수 없는 커스텀 인터페이스다. 이래서 안드로이드가 좋다.


안드로이드의 Smart Launcher를 대학교 입학할 때 구매한 이후로 지금까지 사용하고 있다.


https://play.google.com/store/apps/details?id=ginlemon.flowerpro


간략하면서 보기 좋은 UI가 매력적이다.


PSP의 XMB도 폰으로 쓰기에 괜찮다고 생각한다. 


옆으로 넘기고 위아래로 넘기고.. 나중에 만들어보고 싶다. 



주제 : 모바일 게임 및 UI 디자인


이런 디자인의 게임이면 망할 것이다..



주제 : 캐릭터를 활용한 포스터


반창고 부분은 이름이 들어간 부분이다.


지금보니 되게 성의없어 보인다.



주제 : 캐릭터와 패스를 활용한 디자인


왜 햄토리 였을까?



주제 : 픽셀아트와 손글씨를 이용한 포스터 디자인


SHURE SRH840을 막 샀을 때라 헤드폰에 관심이 좀 있었던 것 같다.




지금보니 뒤로 갈수록 점점 성의없어 보인다.

'호랑이 담배피던 시절에' 카테고리의 다른 글

포트폴리오(취업 이전)  (0) 2021.06.10
2017.02.18.  (0) 2019.02.18
2011.04.04.  (0) 2018.03.26
[Prezi] CG를 꿈꾸다  (0) 2018.03.26

참고 : https://forum.unity.com/threads/shadow-catcher-or-matte-shadow-shader.414418/


홀로렌즈는 MR 컨텐츠를 즐기기 위한 장비이다.


현실 공간에 가상의 오브젝트를 띄우는데, 그림자가 보이지 않는다면 이질감을 주게 된다.


위의 참고 글을 보면 다음과 같은 소스 코드가 있다.



Shader "Unlit/VertColorWithShadow"
{
    Properties
    {
        _MainColor("Main Color", COLOR) = (0,0,0,1)
        _ShadowColor("Shadow Color", COLOR) = (1,0,0,1)
    }
 
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
 
        Pass
        {
            Tags {"LightMode" = "ForwardBase"}
 
            CGPROGRAM
            #pragma multi_compile_fwdbase
            #pragma vertex vert
            #pragma fragment frag
 
            #include "UnityCG.cginc"
            #include "AutoLight.cginc"
 
            float4 _MainColor;
            float4 _ShadowColor;
 
            struct appdata
            {
                float4 vertex : POSITION;
            };
 
            struct v2f
            {
                float4 pos : SV_POSITION;
                float4 mainColor : COLOR;
                LIGHTING_COORDS(1,2)
            };
         
            v2f vert (appdata v)
            {
                v2f o;
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
                o.mainColor = _MainColor;
 
                TRANSFER_VERTEX_TO_FRAGMENT(o);
 
                return o;
            }
         
            fixed4 frag (v2f i) : SV_Target
            {
                fixed atten = 1.0 - LIGHT_ATTENUATION(i);
                fixed3 shadowColor = atten * _ShadowColor.rgb;
                fixed4 finalColor = i.mainColor + fixed4(shadowColor, 1.0);
 
                return finalColor;
            }
            ENDCG
        }
 
        // Pass to render object as a shadow caster
        Pass
        {
            Name "ShadowCaster"
            Tags { "LightMode" = "ShadowCaster" }
     
            ZWrite On ZTest LEqual Cull Off
 
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"
 
            struct v2f {
                V2F_SHADOW_CASTER;
            };
 
            v2f vert( appdata_base v )
            {
                v2f o;
                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }
 
            float4 frag( v2f i ) : SV_Target
            {
                return float4(1.0, 1.0, 1.0, 1.0);
            }
            ENDCG
        }
    }
}

위의 Shader 코드를 Unity에서 작성한다.


Material을 새로 만들고 Shader를 집어넣는다.


Shadow Color를 적절하게 조절해준다.


Spatial Mapping Manager의 Surface Material에 위의 Material을 넣어준다.


그러면 홀로렌즈로 볼 때는 그림자가 정상적으로 나오는 것을 볼 수 있다.


하지만 디바이스 포탈을 통해 캡처나 녹화를 하면 다음과 같이 나온다.



공간이 어둡게 된 부분은 Spatial Mapping이 된 부분이고 밝은 부분은 Mapping이 되지 않은 부분이다.


홀로렌즈 시연을 할 때, 디바이스 포탈이나 스펙데이터 뷰를 사용한 녹화가 필요한데 이런 현상이 일어나면 곤란하다.


위의 코드 일부를 수정하면 간단하게 해결된다.

            fixed4 frag (v2f i) : SV_Target
            {
                fixed atten = 1.0 - LIGHT_ATTENUATION(i);
		if(atten > 0)
		{
 	               fixed3 shadowColor = atten * _ShadowColor.rgb;
        	       fixed4 finalColor = i.mainColor + fixed4(shadowColor, 1.0);
 
 	               return finalColor;
		}
		else
			return float4(0, 0, 0, 0);
            }





그림자 색감을 좀 더 부드럽고 퍼지는 느낌을 주고 싶지만 아직은 홀로렌즈의 성능이 낮아서 불가능하다.



Spatial Mapping에 있는 기능 때문인건지 InputManager의 기능인 것인지는 모르겠지만


OnInputClicked가 실행되면 Spatial Mapping의 Material이 켜지거나 꺼진다.


이를 해결하는 다양한 방법이 있겟지만 다음과 같은 방법을 이용하여 해결하였다.



SpatialMappingManager의 해당 부분을 drawVisualMeshes = value; 부분을 주석처리 하면 된다.


내부를 건드려서 좋은 방법은 아닌 것 같지만 일단 다음과 같이 진행하였다.

http://goodjoon.tistory.com/262


크라우드 펀딩 + 블록체인..


이번에 기획서로 쓰고자 했던 내용인데 너무나도 비슷한 글이 하나 발견되었다.


블록체인을 이용하여 무엇을 할 수 있을까? 하는 고민으로 다양한걸 생각해보지만


너무나 많은 종류의 코인이 등장해서 찾기가 너무 힘들다.


Circle Hough Transform : https://en.wikipedia.org/wiki/Circle_Hough_Transform


컴퓨터비전이라는 과목에 대한 과제이다.


Circle Tough Transform을 이용하여 원을 그리면 된다.


반지름을 알지 못하는 원에 대한 Circle Hough Transform의 간략한 코드는 위키 사이트를 참고한다.


OpenCV에 이미 Circle Hough Transform을 알아서 해주는 기능이 있지만 이를 이용하지 않는 과제이다.







예제로 받은 코드는 다음과 같다.


다만, 조금 이상한 부분이 존재한다.


해당 과목을 진행하시는 교수님은 MATLAB을 주로 사용하는 것으로 안다.


그래서 동적 배열이 적용되는 이미지 배열 기능을 넣지 않았나 싶다.


먼저 이에 해당 하는 부분을 수정하도록 한다. 주어진 과제만 수정하고자 한다면 딱히 의미는 없다.




int *vote = new int[width*height*rad_max];



이미지에서 화면 밖으로 벗어나지 않는 원을 찾고자 한다면


반지름의 최대값은 특정 값으로 제한된다.



const int rad_max = min(width, height) / 2;



그렇다면 배열 초기화에 대한 코드는 다음과 같이 바뀐다.



	for (int i = 0; i < width; ++i)
	{
		for (int j = 0; j < height; ++j)
		{
			for (int k = 0; k < rad_max; ++k)
			{
				vote[i*(height*rad_max) + j * (rad_max)+k] = 0;
			}
		}
	}


예제 코드에서는 캐니 알고리즘이 contours에 미리 적용되어져 있다.


이를 활용하여 Circle Hough Transform에 대한 voting을 구현한다.


Wiki에 나와있는 기본 공식을 참고하여 작성하면 다음과 같다.



	for (int i = 0; i < width; ++i)
	{
		for (int j = 0; j < height; ++j)
		{
			if (contours.at<uchar>(j, i) == 0)
				continue;
			for (int k = rad_min; k < rad_max; ++k)
			{
				for (int t = 0; t < thetha; t++)
				{
					int a = i - k * cos(t * PI / 180);
					int b = j - k * sin(t * PI / 180);
					if (a >= 0 && a <width && b >= 0 && b<height)
						vote[a*(height*rad_max) + b * rad_max + k] += 1;
				}
			}
		}
	}


그리는 부분은 주석을 참고하여 작성하면 된다.



	for (int i = 0; i < width; i++)
	{
		for (int j = 0; j < height; j++)
		{
			for (int k = rad_min; k < rad_max; k++)
			{
				if (vote[i*(height *rad_max) + j * rad_max + k] > threshold)
				{
					Point center(i, j);
					circle(image, center, k, Scalar(255, 0, 0), 1);
				}
			}
		}
	}


여기까지 해주면 기본은 완성이다.


다만 처음할 때 불편했던 점이 있었는데, Threshold를 임의로 찾아야 한다는 점이 불편했다.


정확한 Threshold를 찾는 법에 대해서는 아직 모르겠고, 좀 더 쉽게 Threshold 범위를 유추하는 법을 찾고자 한다.



int threshold = -1;
float threshold_persent = 0.8f;
int real_threshold = 0;


먼저 변수를 몇개 추가했다.


theshold 초기값을 -1로 한 이유는 아래의 변수들을 이용하기 위함이다.


threshold_persent는 최대 vote값에서 몇 % 아래를 threshold로 잡을 것인지 정하기 위한 변수이다.


real_threshold는 %를 통해 일정 범위를 유추했따면 좀 더 세세한 변수를 찾아내기 위한 변수이다.



	for (int i = 0; i < width; ++i)
	{
		for (int j = 0; j < height; ++j)
		{
			for (int k = rad_min; k < rad_max; ++k)
			{
				if (vote[i*(height*rad_max) + j * (rad_max)+k] > threshold)
				{
					threshold = vote[i*(height*rad_max) + j * (rad_max)+k];
				}
			}
		}
	}
	//포괄적인 수정값
	int maxvote = threshold;
	threshold *= threshold_persent;
	//세부적인 수정값
	if (real_threshold != 0)
		threshold = real_threshold;


먼저 최대 vote 값을 찾아낸 후, 여기서 일정 % 아래값을 theshold로 잡는다.


real_threshold 값을 설정했다면 해당 값을 threshold로 잡는다.


real_theshold 값을 정했다면 위의 반복문을 수행하지 않아도 되나, maxvote 값을 확인하기 위하여 유지하였다.



	printf("max vote : %d ", maxvote);
	if (real_threshold == 0)
		printf("range : %.1f%% ", (1-threshold_persent)*100);
	printf("threshold : %d\n", threshold);




처음 20%의 범위를 통해 207에서 전체 원이 다 보인다는 것을 확인했고, 수치를 조금씩 올려본 결과 212가 가장 적절한 threshold임을 알아냈다.


원에서 살짝 어긋나보이는 것은 voting을 할 때의 a, b의 계산된 값이 int로 들어오는 것으로 인한 한계라고 예상된다.


threshold를 쉽게 찾아내고 싶었지만 아직 정확한 방법을 찾지 못했기에 다음과 같이 진행하였다.





첫 과제였기에 조건(Circle Hough Transform)에 대한 내용보다는 그 외 코드 수정 및 부가 기능에 대한 내용만 많이 작성되어져 있다.




t * PI / 180 이 아니라 PI*(t/180)은 왜 안되냐는 질문이 있었다.


int / int 일 때 값은 무조건 int이다.


PI * t를 먼저 계산하면 double이 되고 double / int를 하면 double이다.




a, b에 넣을 때는 다시 int가 된다. 이 때 a, b는 자동으로 floor 값을(내림값)을 받는다.


a, b에 집어넣기 전에 계산된 값을 round 함수를 이용하여 반올림을 하면 좀 더 정확하지 않겠냐는 의견이 있었다.


맞는 것 같아서 테스트를 해봤다.


threshold 값을 모든 원이 보일 수 있는 최대값으로 설정하였으나 선이 두꺼워졌고


원 안쪽으로 살짝 들어간 선이 몇개 있어서 사용하지 않았다.


이미지가 작아서 그런지는 모르겠지만 조금 고민해볼 문제라고 생각한다.




코딩하는 것보다 블로그에 글 올리는게 더 어렵다.


< > 기호 두개 때문에 자꾸 코드가 이상하게 들어간다.


highlight.js를 사용하지 않고 그냥 이미지로 붙여넣을지 고민중이다.

+ Recent posts