본 포스팅은 https://www.deeplearning.ai/ 에서 공개한 무료 gpt 강의의 프롬프팅을 잘 하기 위한 방법을 실습해보고 번역 및 요약해본 포스팅입니다. 

질문을 잘하기 위헤서는 모델의 약점을 파악해서
모델의 실수를 줄여나갈 수 있도록 프롬프팅을 하는 편이 확실히 도움된다는 사실을 학습하게 되었습니다.

 

 대주제1. 프롬프팅의 원칙

 

 -원칙1. Write clear and specific instructions

여기서 말하는 clear라는 것은 명확하게라는 뜻 입니다.
이를 short instructions 와 혼동하지 말라는 의미입니다.

clear하고 specific 한 설명문을 작성하기 위한 전략이 필요합니다.

자세한 내용은 세부 전략을 보고 파악하겠습니다.

 

 -원칙1-전략1. delimiters를 사용하자

delimiters는 구분문자 라는 뜻으로
Triple quotes(삼중 따옴표): """
Triple backticks(삼중 백틱): ```
Triple dashes(삼중 대시): ---
Angle brakets(꺾쇠 괄호): <>
XML tags(XML 태그): <tag> </tag>

등을 말합니다.

예를 들어

요약해야하는 내용 : 
```
교육의 자주성·전문성·정치적 중립성 및 대학의 자율성은 법률이 정하는 바에 의하여 보장된다. 
모든 국민은 직업선택의 자유를 가진다. 모든 국민은 법률이 정하는 바에 의하여 공무담임권을 가진다.
국가는 전통문화의 계승·발전과 민족문화의 창달에 노력하여야 한다. 
모든 국민은 근로의 권리를 가진다. 
국가는 사회적·경제적 방법으로 근로자의 고용의 증진과 적정임금의 보장에 노력하여야 하며, 
법률이 정하는 바에 의하여 최저임금제를 시행하여야 한다.
```

이렇게 프롬프트를 생성한다면 ai가 대답할 프롬프트에 delimiters 안에 있는 내용을 "주입"하는 것이 가능해집니다.

이는 뒤에서 보게 될

ai에게 시간을 좀 더 주고 리소스를 쓰게 만들어 좀 더 좋은 판단을 내리도록 하는 전략의 핵심입니다.

 

 -원칙1-전략2. Ask for a structured output

프롬프팅에서는
구조화된 output을 지정하는 것이 가능합니다.
예를 들어 html, json 등으로 지정할 수 있다는 뜻입니다.

"""
Generate a list of three made-up book titles along \ 
with their authors and genres. 
Provide them in JSON format with the following keys: 
book_id, title, author, genre.
"""

이와 같은 식으로 프롬프트를 생성해서 질문을 할 수 있습니다. 그 결과는?

[
  {
    "book_id": 1,
    "title": "The Lost City of Zorath",
    "author": "Aria Blackwood",
    "genre": "Fantasy"
  },
  {
    "book_id": 2,
    "title": "The Last Survivors",
    "author": "Ethan Stone",
    "genre": "Science Fiction"
  },
  {
    "book_id": 3,
    "title": "The Secret Life of Bees",
    "author": "Lila Rose",
    "genre": "Romance"
  }
]

이런 식으로 제공이 됩니다.
챗 gpt는 이러한 구조를 "이미" 이해하고 있습니다.

이는 프로그래밍에서도 사용이 가능하며 우리가 웹에서 사용하는 챗 지피티에도 똑같이 적용됩니다.

 

 -원칙1-전략3. Ask the model to check whether conditions are satisfied

즉, 모델에게 조건들이 충족되었는지 아닌지 체크하는 질문을 해야한다는 뜻 입니다.

예시 텍스트 :

예시 텍스트 = f"""
차 한 잔을 만드는 것은 쉽습니다! 먼저 물을 끓이세요. \
그 동안 찻잔을 준비하고 차(tea) 가방을 넣으세요. \
물이 충분히 뜨겁다면 차 가방 위에 물을 부어주세요. \
차가 우려지도록 조금 기다리세요. \
몇 분 후에 차 가방을 빼내세요. \
원한다면 설탕이나 우유를 맛에 따라 넣어도 됩니다. \
그리고 완성! 맛있는 차 한 잔을 즐길 수 있습니다.
"""

프롬프트 :

프롬프트 = f"""
세 개의 따옴표로 묶인 텍스트가 제공됩니다. \
그것이 일련의 지시사항을 포함하고 있다면, \
다음 형식으로 지시사항을 다시 작성하세요:

1단계 - ...
2단계 - ...
...
N단계 - ...

텍스트에 지시사항이 포함되어 있지 않다면, \
그냥 "단계가 제공되지 않았습니다."라고 적으세요.
"""

이것은 open ai 에서 제공하는 강의의 예제 프롬프트를 해석한 것 입니다.
~~ 하라는 것을 지시사항이라는 것을 이해하고 있으며

그것이 일련의 지시사항을 포함하고 있다면, 이라는 조건을 붙이고

다음 형식으로 지시사항을 다시 작성하세요:
를 통해서 내가 원하는 포맷을 지정해줍니다.

이런 식으로 if else 처리를 해서 내가 원하는 답을 간결하게 볼 수 있게 되었습니다.

위에서 제공하는 예시 텍스트에 지시하는 느낌이 없는 평서문을 이어 붙인 문장을 주면

No steps provided. 라고만 결과가 나오는 것을 확인할 수 있습니다.

 

 -원칙1-전략4. "Few-shot" prompting

강의에서는 
Give successful examples of completing tasks
Then ask model to perform the task 라고 되어 있는데 이를 적절하게 번역하자면

"성공적으로 완료되었던 적은(few) 예시를 통해 질문하라!" 라고 요약할 수 있습니다.
소수의 샘플을 사용하여 모델을 학습시키는 방법입니다. 이러한 학습 방식은 자연어 처리에서도 이용되는 기법입니다.
간단한 예시를 들면 다음과 같습니다.

프롬프트 내용 :

프롬프트 내용
```
당신의 가장 중요한 임무는 일관된 스타일로 답변하는 것 입니다.

<아이> : 인내에 대해서 알려주세요
<할아버지> : 인내란 것은 말이다~
가장 깊은 계곡을 만드는 강은 소박한 봄에서부터 시작되고
가장 웅장한 교향곡은 하나의 음에서부터 시작되며
가장 복잡한 타페스트리는 단 하나의 외로운 실로부터 시작된단다.

<아이> : 그러면 회복력에 대해서 알려주세요
<할아버지> :
```

이런 식으로 물어보게 되면 할아버지의 입장으로 대답을 해주게 됩니다.

 

 -원칙2.  Give the model time to “think”

모델에게 생각할 시간을 주라는 뜻 입니다. 
예를 들자면 이런 뜻입니다.

바로바로 생각나는 것을 질문하는 것은 잘못된 결론으로 달려가는 지름길

이라고 직원이 언급합니다.
우리는 반드시 모델이 최종 답변을 제공하기 전에
관련된 연쇄적인 혹은 시리즈화 시킨 논리적 추론을 요청해야합니다.

모델에게 일을 시킬 때 너무 복잡한 주제의 문제지만
적은 단어로 적은 양의 시간에 문제를 해결하려고 했을 때 잘못된 정보를 얻은 경험이 많을 것 입니다.
반대로 생각해보면, 이는 아무리 뛰어난 인간이라도 할 수 없는 일 입니다.
예를 들어서 천재라고 불리우는 사람한테 선형대수학과 복잡한 통계에 대한 문제를 길가는 중에 
대뜸 이 문제 풀 수 있냐고 단 한문장으로 물어본다면
그들은 실수할 수 밖에 없습니다. 

그렇기 때문에 우리들은 모델이 좀 더 생각할 시간을 가질 수 있도록 지휘할 필요가 있습니다.
이 말은 ai에게 일을 시킬 때 전산적으로 좀 더 많은 일을 처리할 수 있도록,
당신의 요청을 수행하는데 더 많은 정보를 참조할 수 있도록 질문해야한다는 뜻 입니다.
그 세부 전략과 예제도 함께 보겠습니다.

 -원칙2-전략1. Specify the steps required to complete a task

업무를 완료하는데 필요한 특정한 절차를 요구하세요.

예를 들어서 어떠한 동작을 수행할지 1-, 2- 와 같은 식으로 절차를 붙여서 요청을 한다는 뜻 입니다.

예를 들어서 질문을

Question :
"""
다음 동작을 수행하세요:
1 - 세 개의 역따옴표로 구분된 다음 텍스트를 1문장으로 요약합니다.
2 - 요약본을 프랑스어로 번역합니다.
3 - 프랑스어 요약본에서 각 이름을 나열합니다.
4 - 다음 키를 포함하는 json 객체를 출력합니다: french_summary, num_names.
"""

이런 형식으로 하게 되면
한번에 하나의 대답을 하는 것이 아니라 최종 답변이 4가 되는 것이고
1, 2, 3의 과정을 거치면서 모델에게 정보가 누적되게 됩니다.
4의 출력에서는 1, 2, 3의 과정의 결과들을 참고할 수 있게 되었습니다.

여기에서 우리는 질문을 하게 될 때 결과물의 형식 뿐만 아니라 각각의 단계에 존재하는 객체들의
타입에 대해서도 정해줄 수 있습니다.
예를 들어서 위의 예제를 들어서 설명하게 되면
<다음 텍스트>, <요약본>, <프랑스어 번역본>, <이름> 등의 타입을 지정할 수 있다는 뜻 입니다.
여기에서 <이름>의 타입을 "첫 요약에서 얻은 리스트에 존재하는 이름들" 이런 식으로 지정해줄 수 있다는 뜻 입니다.
타입을 지정하는 방법은 다음과 같습니다.

# 위의 내용과 이어집니다

다음 정해진 형식을 사용하세요:
텍스트 : <요약의 대상이 될 텍스트>
요약본 : <요약한 내용>
번역본 : <요약한 내용을 번역한 내용>
이름들 : <요약한 내용에 포함되어 있는 이름들의 리스트>
결과물 (JSON) : <"요약본"과 num_이름들을 key로 가진 json>

이름이라는 것을 모델이 착각할 수 있기 때문에 이런 식으로 확실하게 지정해주면 모델의 정확도가 올라갑니다.

현재 이름은 원본 텍스트, 요약본, 번역본에 존재하고 있습니다.

이런 식으로 챗 지피티에서도 활용이 가능합니다.

num_name 에 5라고 잘 출력이 되는 모습을 볼 수 있습니다.

 -원칙2-전략2. 모델이 결론을 급하게 내기 전에, 스스로 해결책을 찾도록 지시할 것

다음 사진과 같은 예제를 보게 되면 조금만 계산을 꼬아버려도 모델이 실수를 하는 모습을 볼 수 있습니다.
이는 생각보다 너무 빨리 결론을 내려버리기 때문입니다.

 

위의 사진도 설명을 잘 해낸 것 같지만 올바른 결론을 도출하기 위해서는 좀 더 절차가 필요합니다.
여기에서 학생이 간과한 것은 "평방 피트당 추가로 $10의 유지비용이 발생한다" 라는 점 입니다.
저는 기존에 이런 상황을 만난다면 총 비용을 구성하는 요소들에 포함될 항목들에 대해서 더 자세히 취하는 전략을 사용했는데요, 강의에서 제안한 방식은 상당히 효과적이었습니다.

그렇다면 다음 예제를 살펴보겠습니다.

prompt = f"""
Your task is to determine if the student's solution \
is correct or not.
To solve the problem do the following:
- First, work out your own solution to the problem. 
- Then compare your solution to the student's solution \ 
and evaluate if the student's solution is correct or not. 
Don't decide if the student's solution is correct until 
you have done the problem yourself.

Use the following format:
Question:
```
question here
```
Student's solution:
```
student's solution here
```
Actual solution:
```
steps to work out the solution and your solution here
```
Is the student's solution the same as actual solution \
just calculated:
```
yes or no
```
Student grade:
```
correct or incorrect
```

Question:
```
I'm building a solar power installation and I need help \
working out the financials. 
- Land costs $100 / square foot
- I can buy solar panels for $250 / square foot
- I negotiated a contract for maintenance that will cost \
me a flat $100k per year, and an additional $10 / square \
foot
What is the total cost for the first year of operations \
as a function of the number of square feet.
``` 
Student's solution:
```
Let x be the size of the installation in square feet.
Costs:
1. Land cost: 100x
2. Solar panel cost: 250x
3. Maintenance cost: 100,000 + 100x
Total cost: 100x + 250x + 100,000 + 100x = 450x + 100,000
```
Actual solution:
"""
response = get_completion(prompt)
print(response)

``` ``` 백틱 3개로 감싼 영역이 코드를 입력하는 곳처럼 표시가 되었습니다.
우선 질문까지는 위와 동일합니다.
그런데 여기에서 조건을 달았습니다.
그것은 바로 문제를 직접 풀어보지 않고 학생의 솔루션이 정확한지 결정하지 말라는 것 입니다.
원래 이러한 모델들은 빠르게 결론을 내려는 경향이 있습니다.
그러한 결론을 내는 행위 자체를 지연시킵니다.
이러한 생각하는 방식은 여러 방면으로 응용될 수 있으며 강력합니다.

아까 말했던 "내용 주입"이라는건 다른 무수히 많은 정보들보다 ``` ``` 안의 내용을 우선적으로 점검한다는 뜻 입니다.

지금까지 제공된 내용을 바탕으로 프롬프트를 구성한다면

모델이 제공하는 답변의 정확도가 올라갈 것 입니다.

결정을 유보한 결과

결정을 유보하고 스스로 정리한 정보를 바탕으로 학생이 잘못 되었다는 옳은 판단을 내렸습니다.

 

 대주제2. Model Limitations


여기에서는 세부적인 전략은 없습니다.

이는 현재 모델의 약점, 제한 사항에 대한 내용입니다.
이러한 언어 모델을 사용하여 개발할 때 "모델의 환각(Hallucinations)" 현상이 일어날 수 있음을 생각해야만 합니다.
이 현상은 낯설고 잘 모르는 주제라고 하더라도 대답하려고 그럴듯한 답변을 만드는 과정에서 자주 나타납니다.

모델이 교육을 하면서 방대한 양의 지식에 노출되지만, 이들을 완벽하게 기억하지는 못합니다.
따라서 지식의 경계, 즉 진실과 거짓의 판단부터 정확도에 대한 것들을 의미합니다.

예를 들어서 "에이스 침대"의 "파로마 브릭스 LED 모션베드에 대한 설명"를 요구하면
꽤나 그럴싸하게 말을 지어냅니다.

지금과 같은 경우는 "에이스 침대"는 실제로 존재하는 회사 이름이고
"파로마 브릭스 LED 모션베드"의 경우는 실제로 존재하지만 "시몬스 침대"의 제품입니다.
하지만 모델은 이를 착각하여 설명합니다.

또한 그럴듯한 경우라면 이 세상에 존재하지 않는 상품이라도 그럴듯하게 이야기를 지어냅니다.

 

이것이 위험한 이유는
"정말로 그럴듯해보여서"

만약에 어떤 업무를 하다가 귀찮은 나머지 더블체크를 하지 않았다면
모델의 환각 현상이 일어났던 부분을 나중에 찾아내기란 많이 힘들 수 있습니다.

그래도 강의에서는 지금까지 배운 원칙들을 중심으로 프롬프팅을 진행하면
이러한 현상을 최대한 막을 수 있고,

이러한 텍스트 기반의 모델이 최신 정보 혹은 무언가를 참고해서 
답변을 생성하도록 할 때 가장 좋은 원칙은

1. 먼저 모델에게 관련 인용구를 찾도록 요청하거나
2. 공식문서, 설명서 등의 내용을 긁어와서 모델에게 그 내용은 어떠한 공식문서다, 어떠한 설명서다 설명해주는 방법이 있습니다.

이렇게 또 다른 최신 아티클 혹은 문서를 가져와서 학습 시켰을 경우에는 

- 원하는 인용구를 찾아서 ``` ``` 안에 넣도록 합니다.
- 그 인용구를 찾기 전에는 최종 결론을 내리지 않도록 지시합니다.
- 새로운 문서에서 요청한 인용구를 찾고 선결과제를 끝낸 이후에 그 정보를 바탕으로 답변하도록 설정합니다.

이러한 과정들을 통해 모델의 환각 현상을 상당수 줄일 수 있습니다.

다음 포스팅으로는 좀 더 테크닉적인 부분을 다루게 됩니다.

이번 포스팅까지는 코딩을 잘 모르는 일반인이라도 프롬프팅이 무엇이고 어떻게 하면 좋은지에 대한 좋은 이정표가 될 것이라 생각합니다. 좀 더 자세한 정보를 얻고 싶으신 분은 현재 Open Ai 에서 무려 직원분께서 무료로 강의를 제공하니 참조하시면 좋을 것이라 생각합니다.

깃허브에서 받은 프로젝트를 돌려보기 위해서 readme.md를 읽고
프로젝트를 분석해서 라이브러리들을 빌드하고, make를 해봤으나....!!
엄청 긴 에러 로그를 만나게 되었다.
처음엔 원인이 무엇인지도 모르고 뻘짓을 많이 하긴했었지만, 결국 에러 로그는 무언가 잘못한게 있기 때문에 나오는 것이니까 하나하나 파헤쳐보던 중에 알게 된 사실이 있다.

"C언어 makefile에서 라이브러리를 링크하는 과정에서는 그 순서도 정말 중요하다."

그래서 같은 라이브러리를 import 한다고 하더라도 그 순서에 따라서 빌드가 성공할수도, 실패할수도 있는 것이다.

이게 내가 기획하고 처음부터 빌드한 프로젝트면 몰라도 다른 사람이 만든 프로젝트를 다운 받아서 빌드해볼 때는 꽤나 자주 겪을 수 있는 문제라 기록을 남기려고 한다.

 ldd - 실행 파일 또는 공유 라이브러리 파일의 의존성 체크

 

"ldd program_name" 명령어를 실행하면 program_name이 의존하는 라이브러리들을 확인할 수 있습니다. 
일반적인 경우 ldd는 LD_TRACE_LOADED_OBJECTS 환경 변수를 사용하여 표준 동적 링커를 호출합니다.
이렇게 하면 동적 링커가 프로그램의 동적 종속성 및 찾기를 실행하여 이를 충족하는 객체를 로드합니다.

ldd가 a.out 공유 라이브러리에서 작동하지 않는다는 이슈를 확인했습니다.
이는 매우 오래된 일부 a.out 프로그램에서 작동하지 않기 때문에 언급되었던 이슈입니다. ldd 자원이 컴파일러 릴리스에 추가되기 전에 빌드 되었습니다. 

일부 ldd 버전은 종속성 정보를 얻으려고 시도할 수 있습니다.
그렇기 때문에 신뢰할 수 없는 실행 파일에 ldd 를 사용해서는 안됩니다. 보안 이슈가 있습니다.

ldd에도 옵션이 존재합니다.

--version ldd 
              의 버전 번호를 인쇄합니다 .

       -v , --verbose
              예를 들어 기호를 포함한 모든 정보를 인쇄합니다.
              버전 정보.

       -u , --미사용
              사용하지 않는 직접 종속성을 인쇄합니다. (glibc 2.3.4부터.)

       -d , --데이터-재배치
              재배치를 수행하고 누락된 개체를 보고합니다(ELF
              오직).

       -r , --function-relocs
              데이터 개체와 기능 모두에 대한 재배치를 수행합니다.
              누락된 개체 또는 기능을 보고합니다(ELF만 해당).

       --help 사용 정보.

 

https://man7.org/linux/man-pages/man1/ldd.1.html

참조

 

ldd(1) - Linux manual page

ldd(1) — Linux manual page LDD(1) Linux Programmer's Manual LDD(1) NAME         top ldd - print shared object dependencies SYNOPSIS         top ldd [option]... file... DESCRIPTION         top ldd prints the shared objects (shared libraries) r

man7.org

 

 readelf - ELF 파일이 의존하는 라이브러리의 정보 확인

ELF(Executable and Linkable Format)는 실행 파일, 목적 파일, 공유 라이브러리 그리고 코어 덤프를 위한 표준 파일 형식이다. 1999년 86open 프로젝트에 의해 x86 기반 유닉스, 유닉스 계열 시스템들의 표준 바이너리 파일 형식으로 선택되었다. - 위키 백과

ELF 파일은 코드와 데이터를 여러 섹션으로 구분하고, 각 섹션은 읽기 전용, 쓰기 가능 또는 실행 가능 등의 특정 권한을 가질 수 있습니다. 또한 ELF 파일은 컴파일된 코드 및 데이터 외에도 기호(symbol)와 같은 디버그 정보를 포함할 수 있으며, 동적 링크를 지원하기 위해 필요한 링크 섹션을 가지고 있습니다.

ELF에 대해서는 공부하고 정리할게 많아서 추후에 한번 더 정리할 계획이다.

 

 objdump -  GNU 바이너리 유틸리티의 일부

objdump 는 GNU 바이너리 유틸리티의 일부로서, 라이브러리, 컴파일된 오브젝트 모듈, 공유 오브젝트 파일, 독립 실행 파일 등의 바이너리 파일들의 정보를 보여주는 프로그램이다. objdump는 개체 파일 및 실행 파일을 검사하는데 일반적으로 사용되는 command line tool 입니다. objdjump 는 ELF 파일을 어셈블리어로 보여주는 역어셈블러로 사용될 수 있습니다. 역어셈블러는 기계어를 어셈블리어로 변환하는 컴퓨터 프로그램으로서, 리버스 엔지니어링 도구 중 하나로 볼 수 있는데, 이번 포스팅에서는 생략하도록 하겠습니다.

objdump 를 사용해서 얻을 수 있는 이점 중의 일부를 3가지 정도만 언급하겠습니다.

1. 디버깅 : objdump를 사용하여 실행 파일의 내용을 검사하고 기호 누락, 잘못된 재배치 정보 또는 프로그램이 충돌하거나 잘못 작동할 수 있는 기타 문제와 같은 문제를 찾을 수 있습니다.
2. 리버스 엔지니어링  : objdump를 사용하여 바이너리를 분석하고 작동 방식을 학습할 수 있습니다. 이 도구는 프로그램의 제어 흐름을 이해하고 기능과 변수를 식별하며 보안 취약성을 감지하는 데 도움이 될 수 있습니다.
3. 최적화 : objdump는 프로그램의 성능을 분석하고 코드의; 병목 현상을 식별하는 데 사용할 수 있습니다. 디스 어셈블리를 검사하여 자주 실행되는 코드 섹션을 식별하고 속도를 위해 최적화할 수 있습니다.

그러나 objdump 를 사용하면 몇가지 일어날 수 있는 문제도 있습니다.

1. 복잡성 : objdump 는 바이너리 파일에 대한 많은 정보를 제공하지만 이 정보를 해석하는 것은 어려울 수 있습니다. 기본 아키텍쳐 및 어셈블리 언어에 대한 충분한 이해가 필요합니다.
2. 보안 위험 : objdump 는 바이너리를 리버스 엔지니어링 하는 데 사용할 수 있으므로 공격자가 취약성을 식별하거나 바이너리에서 중요한 정보를 추출하는 데 사용할 수도 있습니다.
3. 호환성 : 다른 버전의 objdump는 이진 파일에 대해 다른 정보를 제공할 수 있습니다. 분석 중인 바이너리와 일치하는 올바른 버전의 objdump를 사용하는 것이 중요합니다.

간단한 예제와 함께 실습해보겠습니다.

file1.c

#include <stdio.h>
#include "header.h"

int main() {
   printf("Hello, World!\n");
   printf("The value of a is %d\n", a);
   return 0;
}


header.h

int a;


Bash

$ gcc -c file1.c
$ gcc -c header.h
$ objdump -p file1.o

 

이렇게 빌드하면 다음과 같은 출력이 나타납니다.

file1.o:     file format elf64-x86-64

.......

 

 pkg-config - 컴파일러나 링커에 필요한 라이브러리 정보를 확인

pkg-config는 C/C++ 소프트웨어 개발에서 사용되는 라이브러리 의존성 관리 도구입니다. 이 도구는 라이브러리의 이름, 버전 및 기타 정보를 검색하여 컴파일러 및 링커가 라이브러리를 찾을 수 있도록 도와줍니다. pkg-config는 소프트웨어 패키지 설치를 간편하게 하기 위해 GNU Build System과 함께 자주 사용됩니다.

pkg-config는 각 라이브러리의 .pc 파일을 읽어서 필요한 헤더 파일, 컴파일러 옵션, 링커 옵션 등을 찾아줍니다. 개발자는 pkg-config를 사용하여 필요한 라이브러리를 검색하고, 해당 라이브러리의 정보를 얻을 수 있습니다. 이를 통해 컴파일 및 링크 단계에서 필요한 라이브러리를 찾을 수 있습니다.

pkg-config를 사용하면 의존성이 있는 라이브러리를 찾기가 쉬워집니다. 또한, 여러 라이브러리를 사용할 때 각 라이브러리의 정보를 일일이 찾아볼 필요가 없습니다. 하지만, pkg-config를 사용하면 각 라이브러리의 .pc 파일이 존재해야 하기 때문에, 라이브러리 제작자가 .pc 파일을 작성하지 않았다면 pkg-config를 사용할 수 없습니다. 또한, pkg-config를 사용하면 Makefile 등의 빌드 스크립트를 복잡하게 만들 수 있습니다.

pkg-config는 3가지 정도의 사용법이 있습니다.

1. 라이브러리 설치 여부 확인
    pkg-config --exists <library-name>
    이 명령은 지정된 라이브러리가 설치된 경우 종료상태 0을 반환하고 그렇지 않은 경우 0이 아닌 값을 반환합니다.

2. 라이브러리에 대한 컴파일러 및 링커 플래그 검색
    pkg-config --cflags --libs <library-name>
    이 명령은 지정된 라이브러리에 대해 컴파일 및 링크하는 데 필요한 컴파일러 및 링커 플래그를 출력합니다.

3. Makefile 에서 pkg-config 사용 

    LIBS = `pkg-config --libs <library-name>`
    CFLAGS = `pkg-config --cflags <library-name>`
    
    myprogram: myprogram.c
        gcc $(CFLAGS) -o myprogram myprogram.c $(LIBS)

이런 식으로 Makefile 의 LIBS, CFLAGS 등의 변수에 pkg-config를 사용해서 make를 사용해서
링커 플래그를 검색하는 과정에서 .c 파일들을 컴파일 및 링크할 때 사용할 수 있습니다.


대표적으로 C언어에서 각 파일 간의 의존성을 체크하는 방법 들 중 4가지를 체크해보았습니다.

의존성을 체크할 때 상황에 맞는 명령, 도구를 사용해서 의존성을 체크하고 프로젝트를 빌드할 수 있도록 합시다.

'코딩이야기 > C' 카테고리의 다른 글

[c] Makefile과 make에 대해서 -1-  (0) 2023.03.17

지난번 포스팅에 이어서 짧은 포스팅을 추가로 올리려고 한다.

지금 하는 작업은 하드 코딩에 가깝지만, 응용한다면 데이터를 가공하는데 도움이 될 것이라 생각한다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// 학생, 학년, 학급, 학생 객체를 생성합니다.

let students = new Object();
let gradesX = new Object();
let classesX = new Object();
let studentX = new Array();

// 학생의 수만큼 k 변수로 반복문 돌립니다.
for(let k = 0; k<3; k++){
    let name = "";
    if( k%3 == 0 ){ name = "철수";
    }else if(k%3 == 1){ name = "영희";
    }else name = "동규";

    let studentIndex = {
        student_name : `${name}${k}`,
        student_height : Math.floor( (Math.random() * 120) ) + k,
        student_average : Math.floor( (Math.random() * 80) ) + k,
        student_report : 0 + k
    }
    
    studentX.push(studentIndex);
    
}  // k 변수 for문 종료

// classesX 객체에 key = student 에 value로 studentX 배열을 넣는다.
classesX.student = studentX;


// studentX 는 배열로 만들어둬서
/* classesX.student.description = "공부를 다들 열심히 합니다." */
// 와 같이 key, value를 추가할 수 없습니다.


// gradesX 객체에 key = class1 에 value로 바로 위의 classesX 객체를 넣는다. (객체 안에 객체 혹은 배열을 넣을 수 있다.)
gradesX.class1 = classesX;


// gradesX 객체에 key = class1 에 key가 teacher, 급훈인 value를 하나씩 넣는다. 우항은 string 값을 이용할 수 있어서 변수 혹은 for문 안에서도 가능하다.
gradesX.class1.teacher = "이민정쌤";
gradesX.class1.급훈 = "최선을 다하자";


// 위의 gradeX 객체를 students 객체의 key = grade1 의 value에 할당해준다.
students.grade1 = gradesX;

// 이외에 추가할만한 key, value를 할당해준다.
students.grade1.학주선생님 = "무서운 학주쌤";

// 아까 설정해둔 곳으로 접근해서 key, value 추가도 가능하다.
students.grade1.class1.특이사항 = "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다.";


// 위와 비슷하게 학년 하나를 더 가공해봅니다.
// 원래라면 학생을 request로 얻어오던지 함수를 이용해서 다르게 하는게 맞지만 test를 위한 과정이라 같은 더미데이터를 사용하겠습니다.
let class2 = new Object();
class2 = classesX;
class2.teacher = "배용준쌤";
class2.급훈 = "잘생긴게 최고다";
students.grade1.class2 = class2;
students.description = "오지고 학생들";

console.log( JSON.stringify( students ) );

 

결과는 다음과 같다

{
  "grade1": {
    "class1": {
      "student": [
        {
          "student_name": "철수0",
          "student_height": 19,
          "student_average": 63,
          "student_report": 0
        },
        {
          "student_name": "영희1",
          "student_height": 57,
          "student_average": 25,
          "student_report": 1
        },
        {
          "student_name": "동규2",
          "student_height": 13,
          "student_average": 77,
          "student_report": 2
        }
      ],
      "teacher": "배용준쌤",
      "급훈": "잘생긴게 최고다",
      "특이사항": "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다."
    },
    "학주선생님": "무서운 학주쌤",
    "class2": {
      "student": [
        {
          "student_name": "철수0",
          "student_height": 19,
          "student_average": 63,
          "student_report": 0
        },
        {
          "student_name": "영희1",
          "student_height": 57,
          "student_average": 25,
          "student_report": 1
        },
        {
          "student_name": "동규2",
          "student_height": 13,
          "student_average": 77,
          "student_report": 2
        }
      ],
      "teacher": "배용준쌤",
      "급훈": "잘생긴게 최고다",
      "특이사항": "쉬는 시간엔 시끄럽지만 수업 때는 열심히 합니다."
    }
  },
  "description": "오지고 학생들"
}

 

웹에서 데이터를 받고 전송을 한다면 json 형식과는 뗄레야 뗄 수가 없을 것이다.

지금까지는 간단한 형식의 json 파일만 사용해서 잘 몰랐던 점들이 있었는데, 오늘 연습해보고 기록하면서 json에 좀 더 잘 알 수 있기를 !!!

 

 배열만 이용한 json 만들기 연습

 

보통 이 경우는 이중 혹은 삼중, 그 이상의 배열을 다룰 때 쉽게 활용할 수 있다.

기본적으로 testArr[ i ][ j ][ k ] 이런 식으로 활용이 될 것이다.

예제 코드는 다음과 같다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// Json 형식이 될 빈 리스트 생성
const student = new Array();

// 학년만큼 i 변수를 반복문 돌립니다.
for( let i = 0; i< 2; i++){
    // 학년 배열 생성
    let grades = new Array() ;
    
    // 학급의 수 만큼 j 변수를 반복문 돌립니다.
    for(let j = 0; j<4; j++){
        // 학급 배열 생성
        let classes = new Array() ;

        // 학생의 수만큼 k 변수로 반복문 돌립니다.
        for(let k = 0; k<3; k++){
            // 학생 객체 생성
            let students = new Object() ;
            if( k%3 == 0 ){
                students.student_name = `철수${i}${j}${k}`;
                students.student_height = Math.floor( (Math.random() * 120) ) + k;
                students.student_average = Math.floor( (Math.random() * 80) ) + k;
                students.student_report = 0 + k;
            }else if(k%3 == 1){
                students.student_name = `영희${i}${j}${k}`;
                students.student_height = Math.floor( (Math.random() * 120) ) + k;
                students.student_average = Math.floor( (Math.random() * 80) ) + k;
                students.student_report = 0 + k;
            }else{
                students.student_name = `동규${i}${j}${k}`;
                students.student_height = Math.floor( (Math.random() * 120) ) + k;
                students.student_average = Math.floor( (Math.random() * 80) ) + k;
                students.student_report = 0 + k;
            }
            
            classes.push(students);
        }
        grades.push(classes);
    }
    student.push(grades);
}

// 전체 학생
console.log("전체 학생");
console.log( JSON.stringify( student ) );

// 한 학년의 학생
console.log("한 학년의 학생");
console.log( JSON.stringify( student[0] ) );

// 한 반의 학생
console.log("한 반의 학생");
console.log( JSON.stringify( student[0][0] ) );

// 한명의 학생
console.log("한명의 학생");
console.log( JSON.stringify( student[0][0][0] ) );

 

결과

전체 학생
student_make_json.js:44
[[[{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0},{"student_name":"영희001","student_height":72,"student_average":9,"student_report":1},{"student_name":"동규002","student_height":119,"student_average":62,"student_report":2}],[{"student_name":"철수010","student_height":101,"student_average":10,"student_report":0},{"student_name":"영희011","student_height":81,"student_average":67,"student_report":1},{"student_name":"동규012","student_height":114,"student_average":43,"student_report":2}],[{"student_name":"철수020","student_height":17,"student_average":75,"student_report":0},{"student_name":"영희021","student_height":14,"student_average":75,"student_report":1},{"student_name":"동규022","student_height":111,"student_average":39,"student_report":2}],[{"student_name":"철수030","student_height":59,"student_average":44,"student_report":0},{"student_name":"영희031","student_height":59,"student_average":45,"student_report":1},{"student_name":"동규032","student_height":96,"student_average":25,"student_report":2}]],[[{"student_name":"철수100","student_height":104,"student_average":28,"student_report":0},{"student_name":"영희101","student_height":87,"student_average":70,"student_report":1},{"student_name":"동규102","student_height":39,"student_average":3,"student_report":2}],[{"student_name":"철수110","student_height":88,"student_average":47,"student_report":0},{"student_name":"영희111","student_height":42,"student_average":23,"student_report":1},{"student_name":"동규112","student_height":44,"student_average":6,"student_report":2}],[{"student_name":"철수120","student_height":102,"student_average":19,"student_report":0},{"student_name":"영희121","student_height":119,"student_average":41,"student_report":1},{"student_name":"동규122","student_height":83,"student_average":49,"student_report":2}],[{"student_name":"철수130","student_height":11,"student_average":15,"student_report":0},{"student_name":"영희131","student_height":97,"student_average":46,"student_report":1},{"student_name":"동규132","student_height":121,"student_average":71,"student_report":2}]]]
student_make_json.js:45
한 학년의 학생
student_make_json.js:48
[[{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0},{"student_name":"영희001","student_height":72,"student_average":9,"student_report":1},{"student_name":"동규002","student_height":119,"student_average":62,"student_report":2}],[{"student_name":"철수010","student_height":101,"student_average":10,"student_report":0},{"student_name":"영희011","student_height":81,"student_average":67,"student_report":1},{"student_name":"동규012","student_height":114,"student_average":43,"student_report":2}],[{"student_name":"철수020","student_height":17,"student_average":75,"student_report":0},{"student_name":"영희021","student_height":14,"student_average":75,"student_report":1},{"student_name":"동규022","student_height":111,"student_average":39,"student_report":2}],[{"student_name":"철수030","student_height":59,"student_average":44,"student_report":0},{"student_name":"영희031","student_height":59,"student_average":45,"student_report":1},{"student_name":"동규032","student_height":96,"student_average":25,"student_report":2}]]
student_make_json.js:49
한 반의 학생
student_make_json.js:52
[{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0},{"student_name":"영희001","student_height":72,"student_average":9,"student_report":1},{"student_name":"동규002","student_height":119,"student_average":62,"student_report":2}]
student_make_json.js:53
한명의 학생
student_make_json.js:56
{"student_name":"철수000","student_height":14,"student_average":73,"student_report":0}

 

기왕에 연습겸 만들어본거 헷갈리던 개념도 한번 시험해보았다.

Js에서 for of과 for in 구문의 차이점을 실제로 체감하기가 힘들었는데 오늘의 예제가 도움이 되었다.

 

// 1학년 1반의 모든 학생 출력
console.log("1학년 1반의 모든 학생 출력");
for( let i = 0; i < student[0][0].length; i++ ){
    console.log( JSON.stringify( student[0][0][i] ) );
}

/** 결과
{"student_name":"철수000","student_height":49,"student_average":13,"student_report":0}
student_make_json.js:63
{"student_name":"영희001","student_height":97,"student_average":77,"student_report":1}
student_make_json.js:63
{"student_name":"동규002","student_height":88,"student_average":21,"student_report":2}
*/



// student[0][0] 을 변수화 시킨 후 1학년 1반의 모든 학생 출력
console.log("student[0][0] 을 변수화 시킨 후 1학년 1반의 모든 학생 출력");
let temp = student[0][0];
for( let i = 0; i < temp.length; i++ ){
    console.log( JSON.stringify( temp[i] ) );
}

/** 결과
{"student_name":"철수000","student_height":49,"student_average":13,"student_report":0}
student_make_json.js:70
{"student_name":"영희001","student_height":97,"student_average":77,"student_report":1}
student_make_json.js:70
{"student_name":"동규002","student_height":88,"student_average":21,"student_report":2}
*/



// 반복문을 출력하는 여러가지 방법 중 하나, for of 구문
console.log("반복문을 출력하는 여러가지 방법 중 하나, for of 구문");
temp = student[0][0];
for( let item of temp ){
    console.log( JSON.stringify( item ) );
    console.log( item );
}

/** 결과
{"student_name":"철수000","student_height":49,"student_average":13,"student_report":0}
student_make_json.js:77
{student_name: '철수000', student_height: 49, student_average: 13, student_report: 0}
{"student_name":"영희001","student_height":97,"student_average":77,"student_report":1}
student_make_json.js:77
{student_name: '영희001', student_height: 97, student_average: 77, student_report: 1}
{"student_name":"동규002","student_height":88,"student_average":21,"student_report":2}
student_make_json.js:77
{student_name: '동규002', student_height: 88, student_average: 21, student_report: 2}
*/



// 반복문을 출력하는 여러가지 방법 중 하나, for in 구문
console.log("반복문을 출력하는 여러가지 방법 중 하나, for in 구문");
temp = student[0][0];
for( let item in temp ){
    console.log( JSON.stringify( item ) );
    console.log( item );
}

/** 결과
"0"
student_make_json.js:85
0
student_make_json.js:86
"1"
student_make_json.js:85
1
student_make_json.js:86
"2"
student_make_json.js:85
2
*/

 

이 외에도 맨 위에 동일한 작업이 반복되는 코드를 함수화 시켜서 할 수도 있습니다.

// 데이터 가공해서 또 다른 Json 만들기 연습
// Json 형식이 될 빈 리스트 생성
const student = new Array();

let makeStudentObject=( students, i, j, k )=>{
    let name = "";
    if( k%3 == 0 ){ name = "철수";
    }else if(k%3 == 1){ name = "영희";
    }else name = "동규";
    students.student_name = `${name}${i}${j}${k}`;
    students.student_height = Math.floor( (Math.random() * 120) ) + k;
    students.student_average = Math.floor( (Math.random() * 80) ) + k;
    students.student_report = 0 + k;
    return students;
}

// 학년만큼 i 변수를 반복문 돌립니다.
for( let i = 0; i< 2; i++){
    // 학년 배열 생성
    let grades = new Array() ;
    
    // 학급의 수 만큼 j 변수를 반복문 돌립니다.
    for(let j = 0; j<4; j++){
        // 학급 배열 생성
        let classes = new Array() ;

        // 학생의 수만큼 k 변수로 반복문 돌립니다.
        for(let k = 0; k<3; k++){
            classes.push( makeStudentObject( new Object() , i, j, k ));
        }
        grades.push(classes);
    }
    student.push(grades);
}

/** 결과
[[[{"student_name":"철수000","student_height":85,"student_average":79,"student_report":0},{"student_name":"영희001","student_height":46,"student_average":34,"student_report":1},{"student_name":"동규002","student_height":47,"student_average":18,"student_report":2}],[{"student_name":"철수010","student_height":118,"student_average":13,"student_report":0},{"student_name":"영희011","student_height":33,"student_average":28,"student_report":1},{"student_name":"동규012","student_height":95,"student_average":13,"student_report":2}],[{"student_name":"철수020","student_height":53,"student_average":6,"student_report":0},{"student_name":"영희021","student_height":26,"student_average":80,"student_report":1},{"student_name":"동규022","student_height":112,"student_average":59,"student_report":2}],[{"student_name":"철수030","student_height":71,"student_average":64,"student_report":0},{"student_name":"영희031","student_height":69,"student_average":23,"student_report":1},{"student_name":"동규032","student_height":74,"student_average":24,"student_report":2}]],[[{"student_name":"철수100","student_height":47,"student_average":30,"student_report":0},{"student_name":"영희101","student_height":36,"student_average":66,"student_report":1},{"student_name":"동규102","student_height":109,"student_average":21,"student_report":2}],[{"student_name":"철수110","student_height":39,"student_average":71,"student_report":0},{"student_name":"영희111","student_height":104,"student_average":17,"student_report":1},{"student_name":"동규112","student_height":102,"student_average":41,"student_report":2}],[{"student_name":"철수120","student_height":57,"student_average":29,"student_report":0},{"student_name":"영희121","student_height":22,"student_average":15,"student_report":1},{"student_name":"동규122","student_height":35,"student_average":15,"student_report":2}],[{"student_name":"철수130","student_height":30,"student_average":72,"student_report":0},{"student_name":"영희131","student_height":111,"student_average":41,"student_report":1},{"student_name":"동규132","student_height":105,"student_average":70,"student_report":2}]]]
*/

 

오늘은 json 을 활용해서 해볼 수 있는 간단한 활용과 예제에 대해서 알아보았습니다.

https://codebeautify.org/jsonviewer

 

Best JSON Viewer and JSON Beautifier Online

Online JSON Viewer, JSON Beautifier and Formatter to beautify and tree view of JSON data - It works as JSON Pretty Print to pretty print JSON data.

codebeautify.org

와 같은 사이트에서 json을 보기 좋게 변환해서 볼 수도 있고,

이외에도 json prettifier 라고 구글에 치시면 비슷한 역할을 하는 웹사이트가 이미 많이 서비스 되고 있습니다.

이와 같이 json 파일이 현재 어떻게 되어 있는지 확인할 수 있습니다.

 

반복문으로 배열을 생성하고 json 화 시키려고 할 때 key에 string 값을 줄 수가 없어서 

명시적으로 하다보면 하드코딩이 되어버리곤 합니다.

이번에 사용하듯이 그냥 배열만 가지고 이용하면서 index 값을 통해 운영한다면 훨씬 쾌적하고 빠르게 코딩할 수 있습니다.

+ Recent posts