생성형 AI(Generative AI) 기술을 활용한 Chat GPT 및 Bard 등과 같은 솔루션이 전 세계적으로 각광을 받고 있는 요즘, 영화나 소설 속에서만 보던 미래의 모습이 어느덧 현실로 다가왔음을 느끼게 되는데요. 이번 시간에는 생성형 AI가 주도하는 세상 속으로 들어가기 위한 입구이자 시작점인 '프롬프트'에 대해 알아보고자 합니다.
이미지 생성형 AI Midjourney를 활용하여 제작한 이미지
미래 세계에 도착한 링크
미래 세계에 도착한 링크
Prompt: Link wearing a green hat, highly developed future city, looking surprisedand curious, focus on background, game illustration, fancy dress decoration, careful brush touch --niji 5 --ar 16:9
(Prompt by Kong Myung, Generate by Midjourney)
왜 프롬프트 엔지니어링을 알아야 하는가?
우리는 일상생활에서 다양한 분야의 사람들과 대화를 통해 서로의 의견과 생각을 주고받으며 살아가고 있습니다. 비슷한 환경과 경험을 공유한 지인과의 대화는 형식과 내용에 구애받지 않고도 원활한 소통이 가능하지만, 이해관계가 다른 사람과의 대화는 형식과 내용을 갖췄음에도 불구하고 의도하는 바가 명확히 전달되지 않아 난감했던 경험이 있을 겁니다.
이미지 생성형 AI Midjourney를 활용하여 제작한 이미지
대화가 되지 않아 다투는 링크와 아이언맨
대화가 되지 않아 다투는 링크와 아이언맨
Prompt: Link wearing the green hat, iron man, frustrated, facing each other,game illustration, fancy dress decoration, careful brush touch --niji 5 --ar 16:9
(Prompt by Kong Myung, Generate by Midjourney)
이렇듯 대화는 단순히 무엇을 전달하느냐보다 상대방에 맞춰 어떻게 내용을 전달할 것인가가 더 중요하다고 얘기할 수 있을 것 같습니다. 프롬프트를 작성한다는 것 역시 대화를 통해 원하는 답변을 구한다는 측면에서 일상 속 대화와 그 형태와 목적이 유사한데요. 그렇기에 AI 모델에게 원하는 바를 명확하게 전달하여 원하는 결과를 얻기 위해서는 AI 모델이 지닌 특성을 이해하고 이에 맞춰 프롬프트를 작성하는 프롬프트 엔지니어링에 대해 알아야 할 것입니다. 그렇다면 본격적으로 '프롬프트'는 무엇이고 또 '프롬프트 엔지니어링'은 무엇을 얘기하는 것인지에 대해 알아보도록 하겠습니다.
프롬프트란?
프롬프트는 '특정한 작업의 수행을 돕는 메시지'란 뜻을 가지고 있으며, 연극이나 TV 프로그램에서뿐만 아니라 컴퓨터가 사람의 언어를 이해할 수 있도록 지시하는 DOS 및 CLI(Command Line Interface) 명령어로서 그 역할을 수행해 왔습니다. 최근 자연어 처리(NLP, Natural Language Process) 기술의 비약적인 발전으로 컴퓨터가 자연어를 이해할 수 있게 됨으로써, 기존 명령어 방식의 질의에서 자연어를 통해 보다 개방적이고 자유로운 방식으로 AI 모델에게 작업을 요청할 수 있게 되었습니다. 자연어 텍스트로서 프롬프트의 영역이 확장된 것이죠.
'특정 작업을 수행하도록 요청하는 명령어 또는 자연어 텍스트'
프롬프트 엔지니어링이란?
프롬프트 엔지너이랑이란 자연어 처리(NLP)기술 분야의 개념으로 AI 모델이 생성하는 결과물의 품질을 높일 수 있는 프롬프트 입력 값들의 조합을 도출해 내는 일련의 프로세스를 얘기합니다. 컴퓨터가 사람의 언어를 이해할 수 있게 되었지만 각 AI 모델이 보유한 학습 데이터의 양과 수준 그리고 특성이 저마다 달라 어떤 AI 모델에게 어떤 방식의 요청을 했느냐에 따라 전혀 다른 수준의 결과를 얻게 됩니다. 이것이 바로 우리가 프롬프트 엔지니어링을 알아야 하는 이유이기도 한데요. AI 모델이 지닌 특성을 이해하고 이를 바탕으로 각 AI 모델에 적합한 프롬프트들의 조합을 만들어내어, 정확도 높은 결과물을 도출 해는 것 그것이 바로 프롬프트 엔지니어링일 것입니다.
'원하는 결과를 얻기 위해 질의를 하는 일련의 과정'
어떤 프롬프트가 좋은 프롬프트인가?
그렇다면 어떤 프롬프트가 좋은 프롬프트일까요? 사실 이 부분에 대해서는 어떤 기준으로 프롬프트를 평가할 것인가에 따라 대답이 달라질 수 있을 것 같습니다. 하지만 누군가 저에게 '일반적으로 좋은 프롬프트란 무엇인가요?'라고 묻는다면 첫째, 원하는 결과를 도출해 낼 수 있는 프롬프트. 둘째, 동일한 프롬프트를 반복하여도 유사한 수준의 결과를 도출해 낼 수 있는 프롬프트라고 대답할 것 같습니다. 아무리 구조화 및 최적화가 잘 되어있는 프롬프트일지라도 원하는 결과물을 도출하지 못한다면 어떤 의미가 있을까요?
이미지 생성형 AI Midjourney를 활용하여 제작한 이미지
깨달음에 웃음 짓는 링크
깨달음에 웃음 짓는 링크
Link wearing a green hat, looking straight ahead, smile, shaking one's head, game illustration, fancy dress decoration, careful brush touch --niji 5 --ar 16:9
(Prompt by Kong Myung, Generate by Midjourney)
물론 동일 수준의 결과물을 얻을 수 있다는 가정하에 '구조화가 잘 되어있어 유사 업무에 재사용이 가능한가?' 또 '결과물을 얻기까지 사용된 토큰이 최적화되었는가?' 등으로 기준을 보다 세부적으로 가져갈 수도 있을 겁니다. 단순하다고 하면 단순하고 파고들고자 하면 한없이 심오한 프롬프트의 세계. 생성형 AI 시대를 살아가는 우리가 프롬프트 엔지니어링을 알아야 하는 이유가 바로 여기 있지 않을까란 생각을 해봅니다.
누구나 프롬프트 엔지니어가 될 수 있다? '모두의 프롬프트 경진대회'
저는 삼성SDS에서 DevRel 업무를 수행하며 새로운 기술을 센싱하고 트렌드에 발맞춰 해당 기술이 개발 문화 또는 일하는 문화로서 사내 정착될 수 있도록 다양한 행사와 이벤트를 기획 및 운영하고 있습니다. '23년의 뜨거운 감자는 누가 뭐라 해도 생성형 AI였는데요. 올해는 이와 같은 트렌드에 맞춰 생성형 AI를 활용한 '모두의 프롬프트' 경진대회를 개최하게 되었습니다.
'모두의 프롬프트' 경진대회는 업무에 활용 가능한 프롬프트를 구현하고 그 아이디어의 독창성과 기술력을 겨루는 대회입니다. 모든 임직원이 프롬프트를 직접 사용해 봄으로서 '프롬프트를 활용해 일하는 문화가 사내에 정착'될 수 있도록 하는데 그 목적이 있었습니다. 대회는 10월 23일부터 11월 10일까지 총 3주간 진행되었으며 개발에서부터 운영, 일반 업무에 이르기까지 약 500여 개의 창의적인 아이디어들이 제출되었습니다.
대회 중 기억에 남는 아이디어는?
대회를 통해 기술력과 독창성이 뛰어난 아이디어들이 대거 출품되었는데요. 그중 가장 기억에 남는 아이디어는 '답도 없는 구닥다리 웹사이트에 보안까지 하라고?'란 제목의 아이디어였습니다. 제목만 보아도 오래된 레거시 시스템의 보안 취약점을 해결해야 하는 운영자의 애환이 잘 느껴졌는데요. 작명 센스와 더불어 프롬프트를 통해 문제를 식별하고 해결해 나가는 방식이 독창적이어서 특히 기억에 남았던 것 같습니다.
1. 해결 방안 모색
(생성형 AI와 대화 주고 받은 화면)
운영자 : 우리 서버는 Apache 라이브러리를 쓰고 있으니 StringEscapeUtils.escapeSql() 함수 사용은 가능해. 하지만 소스코드마다 일일히 찾아 PreparedStatement 로 변환하거나 StringEscapeUtils.escapeSql() 함수를 반영하는 건 너무도 번거로운 일이야.
생성형 AI : 말씀하신 것처럼, 코드마다 일일히 찾아 PreparedStatement로 변환하거나 StringEscapeUtils.esxapeSql() 함수를 반영하는 것은 매우 번거로운 일입니다. 이러한 문제를 해결하기 위해서는 다음과 같은 방법들이 있습니다.
1. AOP를 이용하여 자동으로 이스케이핑 처리하기 AOP(Aspect Oriented Programming)를 이용하여 자동으로 이스케이핑 처리를 할 수 있습니다. AOP를 이용하면, 모든 SQL 문을 실행하기 전에 자동으로 이스케이핑 처리를 하는 어드바이스를 구현할 수 있습니다.
2. ORM 프레임워크를 사용하기 RPM 프레임워크를 사용하면, 객체를 이용하여 데이터베이스에 접근할 수 있습니다. DRM 프레임워크는 데이터베이스에 접근할 때 자동으로 이스케이핑 처리를 해주므로, SQL 인젝션 방어에 도움을 줍니다.
3. 자체 라이브러리를 개발하여 사용하기 자체 라이브러리를 개발하여, 입력값 검증과 이스케이핑 처리를 자동으로 처리할 수 있습니다. 이를 이용하면, 모든 SQL문을 실행하기 전에 자동으로 이스케이핑 처리를 할 수 있습니다.
4. 보안 검사 도구를 이용하여 검사하기 보안 검사 도구를 이용하여, 코드에서 SQL 인젝션 취약점을 검사할 수 있습니다. 검사 도구를 이용하면, SQL 인젝션 취약점을 빠르게 발견하고 수정할 수 있습니다.
위의 방법들을 이용하여, SQL 인젝션 방어를 보다 효과적으로 처리할 수 있습니다.
운영자 : AOP, ORM 적용이나 자체 라이브러리 개발은 현실적으로 힘들어. Java 웹 애플리케이션에서 채택할 수 있는 공통 이스케이프 처리에 대한 좋은 사례를 몇 가지 소개해줘.
2. 코드 작성 요청
(생성형 AI와 대화 주고 받은 화면)
운영자 : 소스마다 고치지 않아도 된다니 급관심이 생기네. SQL 인젝션을 방지할 수 있는 Filter 구현에 대한 코드 예시를 작성해줘.
생성형 AI : 아래는 SQL 인젝션을 방지할 수 있는 Filter 구현에 대한 코드 예시입니다.
@webFilter("/")
public class SQLInjectionFilter implements Filter {
private final static Logger logger = LoggerFactory.getLogger(SQLInjectionFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Map paramMap = httpRequest.getParameterMap();
for (String key : paramMap.keySet()) {
String[] values = paramMap.get(key);
for (String value : values) {
if (isSQLInjection(value)) {
logger.warn("SQL Injection detected: {}", value);
throw new ServletException("SQL Injection detected");
}
}
}
}
chain.doFilter(request, response);
}
private boolean isSQLInjection(String value) {
String[] sqlMetachars = { ";", "*", "--", "/*", "|" };
value = value.toLowerCase();
for (int i = 0; i < sqlMetachars.length; i++) {
if (value.indexOf(sqlMetachars[i]) > 0) {
return true;
}
}
return false;
}
// 생략
}
위 코드는 HTTP 요청의 모든 파라미터에 대해 SQL 인젝션 취약점을 검사하는 Filter입니다. HTTP 요청이 들어올 때마다 doFilter 메서드가 실행되며, HTTP 요청의 파라미터를 가져와 isSQLiInjection 메서드를 이용하여 SQL 인젝션 취약점을 검사합니다.
3. 개선 요청 #1
(생성형 AI와 대화 주고 받은 화면)
운영자 : 더 깔끔한 코드로 작성해 주면 좋겠어. 앞에서 소개해준 StringEscapeUtils.escapeSql() 함수를 활용한 코드로 다시 작성해줘.
생성형 AI : 아래는 StringEscapeutils.escapesql() 함수를 이용한 SQL 인젝션 방어 Filter 코드 예시입니다.
@WebFilter("/")
public class SQLInjectionFilter implements Filter {
private final static Logger logger = LoggerFactory.getLogger(SQLInjectionFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
Map paramMap = httpRequest.getParameterMap();
for (String key : paramMap.keySet()) {
String[] values = paramMap.get(key);
for (String value : values) {
String escapedValue = StringEscapeUtils.escapeSql(value);
if (!value.equals(escapedValue)) {
logger.warn("SQL Injection detected: {}", value);
throw new ServletException("SQL Injection detected");
}
}
}
}
chain.doFilter(request, response);
}
// 생략
}
위 코드는 HTTP 요정의 모든 파라미터에 대해 StringEscapeutils.escapesql() 함수를 이용하여 SQL인젝션 취약점을 검사합니다.
HTTP 요청이 들어올 때마다 doFilter 메서드가 실행되며, HTTP 요청의 파라미터를 가져와 StringEscapeutils.escapesql() 함수를 이용
하여 이스케이핑 처리합니다.
이스케이핑 처리가 된 문자열과 원본 문자열을 비교하여, 이스케이핑 처리가 된 문자열과 다르다면 SQL 인젝션 취약점이 있다는 것
을 알리고 ServletException을 발생시켜 SQL 인젝션을 방어합니다.
이와 같이 Filter를 이용하여 StringEscapeutils.escapesql() 함수를 이용한 SQL 인젝션 방어를 할 수 있습니다. 위 코드는 간단한 예시
일 뿐, 실제 상황에서는 보안성과 성능을 고려하여 보안 처리를 구현해야 합니다.
4. 개선 요청 #2
(생성형 AI와 대화 주고 받은 화면)
운영자 : StringEscapeUtils.escapeHtml4 는 HTML 4 기준인 것 같은데, 호환에 문제가 생길수도 있어서 그 이전 버전의 HTML 에 대해서도 이스케이프 함수들이 존재한다면 추가로 알려줘.
생성형 AI : StringEscapeUtils.escapeHtml4) 함수는 HTML 4 기준으로 특수 문자를 이스케이핑 처리하는 함수입니다. HTML5에서는 몇몇 문자
에 대해 이스케이핑 처리가 변경되었기 때문에 호환성 문제가 발생할 수 있습니다.
따라서, HTML5에서도 이스케이핑 처리가 가능한 StringEscapeUtils.escapeHtml() 함수가 제공됩니다. 이 함수는 HTML 4와 HTML 5
에서 모두 사용할 수 있는 함수입니다.
아래는 StringEscapeUtils.escapeHtml() 함수를 이용한 예시 코드입니다.
@WebFilter("/")
public class SecurityFilter implements Filter {
private final static Logger logger = LoggerFactory.getLogger(SecurityFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
}
Map paramMap = httpRequest.getParameterMap();
for (String key : paramMap.keySet()) {
String[] values = paramMap.get(key);
for (String value : values) {
// SQL Injection 방어
String escapedValue = StringEscapeUtils.escapeSql(value);
if (value.equals(escapedValue)) {
logger.warn("SQL Injection detected: {}", value);
throw new ServletException("SQL Injection detected");
}
// XSS 방어
String escapedHtml = StringEscapeUtils.escapeHtml4(value);
if (!value.equals(escapedHtml)) {
logger.warn("XSS Attack detected: {}", value);
throw new ServletException("XSS Attack detected");
}
}
}
chain.doFilter(request, response);
}
// 생략
}
위 코드는 HTTP 요청의 모든 파라미터에 대해 StringEscapeUtils.escapeSql() 함수를 이용하여 SQL 인젝션 취약점을 검사하고, String EscapeUtils.escapeHtml() 함수를 이용하여 XSS 취약점을 검사합니다.
그 외에도 애플리케이션 리팩토링 및 튜링, 보안 취약점 식별 및 개선 그리고 서버 운영을 위한 리눅스 스크립트 작성 등 업무 관련 다양한 주제의 아이디어가 출품되었으며 이와 같은 프롬프트들로 우리의 워라벨이 보다 나아지길 기대해 봅니다.