유년시절 이야기 (2)
이제 부터가 본격적으로 개발에 입문하게 된 이야기이다.
기능반, 그리고 첫 대회
그렇게 1학년이 끝날 때 쯤, 앞서 언급한 기능반
을 활동을 선생님이 권해주셨다. 그럴 꺼면 그냥 처음부터 하게 해주던가 내가 1학년 1학기 초에 기능반을 하고 싶어서 신청 했을 때는 게임 개발 기능반
만 있었는데, 약 2학기 말에 웹 디자인 기능반
이 생겼고 여기에 가입을 권해주신 것이었다.
나는 가입과 동시에 바로 다음 해 4월에 열리는 지방대회를 준비했다.
기능경기대회
- 정식 명칭은 기능올림픽이다.
- 기능경기대회는 특성화고등학교의 수능이며 축제라고 할 수 있다.
- 지방대회 - 전국대회 - 국가대표 선발전 - 세계대회 등의 과정이 있다.
- 지방대회는 4월에 개최된다. 그런데 올해는 코로나 때문에 6월에 개최되었다.
- 전국대회는 9월/10월 중에 개최된다. 올해는 9월 14일 부터 7일간 대회가 진행되었다.
- 세계대회는 2년에 한 번씩 개최된다. 세계대회 입상 혜택은 다음과 같다. 올림픽 입상 혜택과 동일하다고 보면 된다.
- 병역대체복무
- 신축 아파트 분양권 획득
- 대기업 연봉급 상금
- 대학 진학시 학비 전액 지원
- 연금
- 자세한 내용은 마이스터넷 참고
사실 우리 학교에서 웹 디자인
종목에서의 입상은 고사하고 출전해본 경험 자체가 전무했다. 어떻게 시작해야 할지 몰랐던 선생님과 나를 포함한 기능반 친구들은 일단 인터넷 강의부터 차근 차근 들었다. 이름은 웹 디자인
이지만, 반 이상이 개발과 관련된 내용이었다.
A-Module
사이트 주제에 적합한 기획서 만들기 - 3시간B-Module
기획서를 기반으로 포토샵으로 디자인 하기 - 3시간- 메인 페이지 1개, 서브 페이지 3~4개
C-Module
웹 사이트에서 사용 될 플래시 애니메이션 만들기 - 3시간- 메인 페이지에서 사용될 애니메이션 1개
- 서브 페이지에서 사용될 애니메이션 1개
D-Module
기획서/디자인/플래시를 기반으로 사이트 기본 틀 구축하기 4시간- 퍼블리싱 작업
- 플래시로 메뉴 구현
- PHP + phpmyadmin + MySQL 로 기본 사이트 구축
- DB 설계
E-Module
사이트 완성하기 5시간- 로그인/회원가입/게시판
- 검색
- 달력
- 쇼핑몰 기능(상품 구매 / 상품 예약 / 상품 예약 확인 / 예약 취소)
- 기타 등등
즉, 기획/디자인/플래시(애니메이션/퍼블리싱/DB설계/개발 등 모든 일련의 과정을 단 18시간 안에 수행해야 하는 것이다. 그것도 인터넷 없이 하드코딩으로!
눈을 감아보세요. 캄캄하죠? 그게 당신의 미래입니다.
디자인 공부도, 개발 공부도 정말 처음 해보는 거라서 어디서 부터 어떻게 시작해야할지 막막했다. 방학 직전까지는 어버버 하면서 시간을 때우다가 겨울방학 시작 직후에 학교에서 광주에 있는 호남대학교로 집체교육을 보내줬다.
이 집체교육에서 기능반에서 공부하는 다양한 학교의 사람들을 만났는데 그 중에 양영디지털고등학교
에서 온 선배님과 같은 방을 썼다. 이 후에도 집체교육을 갈 때 마다 양영디지털고등학교 사람들과 마주쳤는데, 나랑 동갑이던 친구가 졸업할 때 즈음 op.gg라는 사이트를 만들었다고 들었다. 싹이 남다른 사람이었다
각설하고, 다양한 학교의 사람들이 모여 특강을 듣는 형태였는데 이 때 특강을 해주신 강사분이 2009년도에 전국대회에서 2등을 했고, 2008년도 입상자들과 2009년도 입상자들과 국가대표 선발전을 거쳐서 선별된 분이었다. 2011년 런던 대회에서 2등을 했고, 지금은 야놀자에서 일하고 계신걸로 알고 있다. 이 분이 아니였으면 지금의 내가 없었을 것이다.
강의 내용은 구체적으로 기억나지 않았지만 강의를 해주시는 분의 손이 매우 빨랐고, 무슨 설명을 하는지 하나도 모르겠지만 그래도 놓치면 큰일 난다는 느낌이 들었다. 무엇보다 강의에 참여한 모든 학새들에게 국가대표 선수가 직접 작성한 소스코드 제공해주었기 때문에 그 자체로 공부할 때 정말 큰 도움이 되었다. 말 그대로 기획서, 디자인, 플래시는 물론 js와 php, mysql 까지 모든 자료를 받았다.
* 2010년에 봤던 웹 사이트의 디자인 파일 (index.psd)
* 2012년에 내가 전국대회를 준비하면서 만든 디자인. 이러한 디자인을 1시간 만에 완성해야 한다.
문제는 기획서나 디자인은 어떻게든 따라 그릴 수 있었으나, 코드는 그렇지 못했다. 어림 잡아도 책 한권 분량의 코드였기 때문에(100줄이 넘는 코드가 약 50개의 파일에 뿔뿔이 흩어져 있었다) 지금 생각해보면 별거 아니지만 그 당시에는 정말 이걸 어떻게 공부해야하나 싶었다.
이게 약 10년전에 내가 처음으로 프로그래밍을 공부하면서 봤던 코드이다.
<?
//세션 시작
session_start();
//엔코딩
function encode($str){
$str = base64_encode($str);
$str = str_replace('/', '^2', $str);
$str = str_replace('+', '$4', $str);
return $str;
}
//디코딩
function decode($str){
$str = str_replace('$4', '+', $str);
$str = str_replace('^2', '/', $str);
$str = base64_decode($str);
return $str;
}
//주소값 저장
$var_array = explode('/', $_SERVER['PATH_INFO']);
$page_mode = $var_array[1];
$midx = $var_array[2];
$sidx = $var_array[3];
$action = $var_array[4];
$idx = $var_array[5];
$parent = $var_array[6];
$page_num = $var_array[6] ? $var_array[6] : 1;
$search_type = isset($var_array[7]) ? $var_array[7] : NULL;
$search_key = isset($var_array[8]) ? urldecode($var_array[8]) : NULL;
$search_date = $var_array[9];
$search_date2 = $var_array[10];
$current = $page_mode && $midx && $sidx ? 'sub' : 'main';
$get_page = "/index.php/{$page_mode}/{$midx}/{$sidx}/";
//메시지 출력
function alert($msg){
echo "<script type=\"text/javascript\">alert('{$msg}');</script>";
}
//페이지 이동
function move($url = false){
echo '<script type="text/javascript">';
echo $url ? "document.location.replace('{$url}');" : 'history.back();';
echo '</script>';
exit();
}
//페이지 액세스
function access($bool, $msg = '해당 페이지를 액세스 할 수 없습니다.', $url = false){
if(!$bool){
alert($msg);
move($url);
}
}
//사용권한 검사
function lv_chk($lv){
$cur_lv = $_SESSION['lv'] ? $_SESSION['lv'] : 3;
if($lv == 2){
$msg = '로그인 후 이용하실 수 있습니다.';
$url = '/index.php/page/member/login/';
} else {
$msg = '사이트 관리자만 접근 할 수 있습니다.';
}
access($cur_lv <= $lv, $msg, $url);
}
//이메일 부호화
function hex($str){
$strlen = strlen($str);
for($i = 0; $i < $strlen; $i++) $hex .= '&#x'.bin2hex(substr($str, $i, 1)).';';
return $hex;
}
//html 엔티티
function safe_html($arr){
if(is_array($arr)){
foreach($arr as $key=>$val) $arr[$key] = htmlspecialchars($val);
return $arr;
}
}
//글자 수 제한
function cut_str($str, $len){
$str = html_entity_decode($str);
$strlen = strlen($str);
if($strlen > $len)$str = substr($str, 0, $len).'..';
return htmlspecialchars($str);
}
//하이라이팅
function hit($str, $keyword){
$str = str_replace($keyword, "<span class=\"search_txt\">{$keyword}</span>", $str);
return $str;
}
//파일 다운로드
function down($file, $file_name, $dir){
$file = urlencode($file);
$file_name = urlencode($file_name);
$dir = "/page/down.php?dir={$dir}&file={$file}&file_name={$file_name}";
return $dir;
}
//컬럼값 추출
function get_column($arr, $cancel){
$cancel = explode('/', $cancel);
foreach($arr as $key=>$val){
if(!in_array($key, $cancel)){
$entity_val = mysql_real_escape_string($val);
$column .= ", {$key}='{$val}'";
}
}
$column = substr($column, 2);
return $column;
}
//쿼리
function query($type, $table, $column){
global $connect;
switch($type){
case 'insert' :
$query[] = "insert into {$table} set ";
break;
case 'update' :
$query[] = "update {$table} set ";
break;
case 'delete' :
$query[] = "delete from {$table} ";
if($column) $query[] = ' where ';
break;
}
$query[] = $column;
$query = implode('', $query);
mysql_query($query, $connect) or die($query.mysql_error());
}
//파일 업로드
function file_upload($file, $dir, $type = 'img'){
if(is_uploaded_file($file['tmp_name'])){
$ex_name = array_pop(explode('.', strtolower($file['name'])));
if($type == 'img'){
$ex_name_chk = array('gif', 'jpg', 'png');
access(in_array($ex_name, $ex_name_chk), '이미지 파일만 업로드 할 수 있습니다.');
} else {
$ex_name_chk = array('html', 'htm', 'php', 'asp', 'jsp', 'exe', 'js');
access(!in_array($ex_name, $ex_name_chk), '업로드가 제한된 확장자 파일입니다.');
}
$date = date('ymdhis');
$rand = rand();
$upload_name = "{$date}_{$_SESSION['id']}_{$rand}.{$ex_name}";
move_uploaded_file($file['tmp_name'], "{$_SERVER['DOCUMENT_ROOT']}/data/{$dir}/{$upload_name}");
return $upload_name;
}
}
//페이지 나누기
function paginate($cur_page, $line, $total, $url, $add_class = ''){
if($total){
$url = explode('&&', $url);
$first_page = 1;
$last_page = ceil($total/$line);
$prev_page = $cur_page - 1;
$next_page = $cur_page + 1;
$paginate = "<p class=\"paginate {$add_class}\">";
$paginate .= $cur_page == $first_page ? '<span title="처음">처음</span>' : "<a href=\"{$url[0]}{$first_page}{$url[1]}\" title=\"처음\">처음</a>";
$paginate .= $cur_page == $first_page ? '<span title="이전">이전</span>' : "<a href=\"{$url[0]}{$prev_page}{$url[1]}\" title=\"이전\">이전</a>";
for($i = 1; $i <= $last_page; $i++){
$paginate .= $cur_page == $i ? "<strong title=\"{$i}\">{$i}</strong>" : "<a href=\"{$url[0]}{$i}{$url[1]}\" title=\"{$i}\">{$i}</a>";
}
$paginate .= $cur_page == $last_page ? '<span title="다음">다음</span>' : "<a href=\"{$url[0]}{$next_page}{$url[1]}\" title=\"다음\">다음</a>";
$paginate .= $cur_page == $last_page ? '<span title="맨뒤">맨뒤</span>' : "<a href=\"{$url[0]}{$last_page}{$url[1]}\" title=\"맨뒤\">맨뒤</a>";
$paginate .= '</p>';
return $paginate;
}
}
?>
lib.php
라는 파일의 내용인데, 이 코드들을 이해하지 못하면 다른 코드는 보나 마나했었다. 그런데 이해를 할 수가 없었다. 이해를 하고 싶어도 도저희 저건 프로그래밍을 제대로 해본적도 없는 내 머리로 감당할 수 있는 양의 내용이 아니었다.
사실 그냥 튜토리얼 수준의 php 코드부터 접했다면 좋아겠지만 내가 받은건 견고하게 짜여진 솔루션 코드였다. 그리고 이 솔루션에는 clean url 이라는 기법이 적용된 상태였는데 /board/view.php?idx=1
이러한 형태의 주소를 /board/view/1
처럼 보일 수 있도록 작업해주는 것이었다.
뿐만 아니라 메인페이지나 서브페이지를 조회하기 위해서는 DB 설계도 이해하고 있어야 했다.
- 모든 페이지를 index.php에서 보여줘야한다.
- DB접속과 라이브러리를 포함한 모든 내용을 index.php에서 가져온다.
/index.php
로 접근하면 메인페이지를 보여줘야 한다./index.php/page/1/1
로 접근하면 DB에서메인메뉴 1
에 대한서브메뉴 1
의 페이지 정보를 가져와서 보여줘야 한다./index.php/page/1
로 접근하면 DB에서메인메뉴 1
의첫 번째 서브메뉴
를 가져와서 보여준다.- 이 때 메뉴의 타입(컨텐츠, 검색, 게시판, 회원 및 기타 커스텀 페이지)에 따라 보여지는 내용이 달라진다.
- 각각의 메뉴에는 접근 가능한 레벨이 존재한다. 회원 가입할 때에도 레벨이 존재한다.
- 메뉴의 레벨이 회원의 레벨보다 높을 경우 접근할 수 없다.
/index.php/page/member/join
은 DB에 접근하지 않고 바로 회원가입 페이지를 보여준다./index.php/page/member/login
은 DB에 접근하지 않고 바로 로그인 페이지를 보여준다./index.php/admin/6/1
로 접근하면 관리자 수준의 레벨을 가진게 아니라면 접근을 방지한다.- 관리자일 경우고 해당 메뉴의 정보와 페이지를 조립하여 사용자에게 보여줘야 한다.
프레임워크를 사용한다면 무척 간단하지만, 프레임워크를 사용하지 않고 하드 코딩으로 위의 내용을 구현하려면 여간 복잡한게 아니였다.
누군가가 Step by Step 형태로 교육을 해주었다면 좋았을텐데, 아쉽게도 프로그래밍을 제대로 아는 선배도 없었고, 선생님은 더더욱 없었다. 모든 것을 처음부터 끝까지 혼자 공부해야했다. 이렇게 공부해야 하는 줄 알았다면 나는 인문계 고등학교를 갔을 것이다
그래서 내가 할 수 있던 일은 거짓말을 보태지 않고 위의 코드(lib.php)를 그대로 외워버렸다. 스페이스바, 따옴표, 여백, 인던트, 라인수 등 모든 내용을 그대로 외웠다. 일단 코드를 제대로 치지 않을 경우 에러가 빵빵 터지는데, 나는 에러를 해석할 줄 몰랐기 때문에 있는 그대로 외워야했다.
이 당시에는 구글 검색도 제대로 할 줄 몰랐으며(검색을 해도 네이버검색..), 나에게 프로그래밍을 가르쳐줄 선배나 선생님도 없었다. 모든 것을 혼자 해야 했다. 그래서 절망했었고, 내가 할 수 있는 일은 그냥 코드를 외우는 방법 밖에 없었던 것이다.
문제는 그렇게 애써가며 코드를 외웠는데 앞서 말했던 것 처럼 이 코드를 제대로 사용하는 방법을 몰랐다. 무엇보다 대회가 있다는 것을 인식한게 12월이었고, 대회는 4월에 치루어졌다.
4개월만에 기획(PPT), 디자인(포토샵,일러스트), 플래시, HTML/CSS, Javascript, PHP 등을 모두 공부해야 했는데 PHP는 고사하고 HTML/CSS도 제대로 할 수 있는 수준이 아니었다.
그래도 내 생에 그렇게 열심히 공부했던 순간이 있었을까 싶을 정도로 죽어라 공부했다. 사실 지금은 일상이지만
대회 때문에 수학여행도 못갔고 보내준다고 뭐 달라지냐고! 방학도 주말도 반납하고 왕복 3시간인데.. 맨날 학교에 갔으며 항상 밤 10시 정도에 학교에서 출발했고, 대중교통을 이용하면서도 계속 코드를 봤다.
그렇게 2011년 4월이 되었고, 성남시에 있는 양영디지털고등학교라는 곳에서 대회를 치루었다. 대회장에서 처음으로 기계식 키보드의 존재를 알았고, 학교 컴퓨터의 사양이 그렇게 좋을 수 있다는 것도 처음 알았다.
어쨌든 나는 똥손을 이용하여 기획서도 만들고, 디자인도 만들고, 플래시도 만들고, 아 그 당시에는 메뉴도 플래시로 만들었다. 웹에서 JS로 플래시에 메뉴 정보에 대한 변수를 넘겨주면, 플래시가 메뉴 정보를 읽어와서 메뉴로 보여주어야 했었다. 그런데 이것도 이해를 못해서 그냥 통으로 외웠다. 본의 아니게 액션스크립트 까지 공부해야 했다.. 빌어먹을
사실 코드만 외운게 아니라 이것 저것 할게 많았지만.. 이젠 뭐 플래시를 제대로 아는 사람도 많이 없기 때문에 생략하겠다.
어쨌든 우여곡절 끝에 플래시까지 어거지로 만들었는데 대회장에서 여태까지 죽어라 외운 코드의 내용이 기억나질 않았다. 그런데 그렇게 외운 코드의 내용이 이제 머릿속에서 어느정도 이해가 되었는지 그냥 내 멋대로 코드를 작성했고 내가 원하는 데이터를 DB에서 뽑아내어 출력하는 것 까지 해낸 것이다.
입상은 하지 못했지만 그 과정이 정말 신기했다. 내가 한건 외운 것 밖에 없었는데, 나도 모르게 코드가 이해되는 현상이 벌어진 것이다.
문제는 코드는 이해한다고 쳐도 Web이라는 개념에 대해 이해한게 아니었기 때문에 Request, Response, GET, POST 같은 용어가 낯설고 생소했으며 그 이후에는 정말 어떻게 해야 할지 도통 알 수가 없었다. 이렇게 나의 첫 대회는 마무리 되었고, 그 뒤로는 정말 암울한 생활이 이어졌다.
방황
2011년에 4월까지 모든 에너지를 소모하며 달려갔는데 어쨌든 결과는 허탈했다. 어느 정도 코드에 대한 이해도는 좋아졌으나 사실 공부할 여력이 생기질 않았다. 기능대회라는 명목으로 동아리가 만들어져서 공부할 공간이 생기긴 했으나, 선생님도 이 대회에 대해 자세히 아는게 아니었고, 프로그래밍을 잘 하는 선배가 있는 것도 아니었다.
마땅한 인터넷 강의가 있는 상태도 아니었고, 그냥 뭐랄까 공부하기가 정말 무척 싫었다. 어쩌면 핑계였을지도 모른다. 그냥 내가 하기 싫은 이유를 주변 상황 때문이라고 탓하는 핑계.
1학년 때는 야자라도 했으나 2학년 때는 기능반을 핑계로 수업이 끝나면 기능반실에서 노닥거렸다. 수행평가가 있으면 수행평가를 했고, 시험기간이면 시험공부를 하고, 그게 아니면 그냥 놀았다. 그 당시에 워크래프트의 랜덤디펜스가 유행이었는데
나는 원랜디(원피스 랜덤 디펜스)를 굉장히 열심히 했다.
이 외에도 드라마를 보거나, 애니메이션을 보거나, 혹은 뭐 간간히 게임도 하면서 시간을 보냈다. 정말 허송세월을 보낸 것이다.
지난 편에 언급했던 버블파이터라는 게임도 열심히 했다. 사실 학교에서 성적이 나쁜 편도 아니었고(졸업할 때 최종 내신이 1.5등급 이었다) 부모님도 공부에 대해 알아서 하겠거니 하며 터치하는 편도 아니었다.
그렇게 2학년 1학기가 그냥저냥 흘러갔고 여름방학 때 또 집체교육을 갔다.
* 사진이고 뭐고 필요 없으니 집에 보내달라고 표정을 통하여 어필하는 중.
집체교육에서 무언가를 하긴 했으나 사실 제대로 기억 나는 부분은 없었다. 다만 내가 남들 보다 오롯이 혼자서 무언가를 많이 했다는 점을 강사분이 인정해주셨고 어깨를 조금 으쓱했다는 점 정도?
그런다고 현실적으로 내가 문제를 잘 풀이 할 수 있는 수준도 아니었기 때문에 그렇게 대단한 일도 아니었다.
그렇게 또 집체교육을 갔다온 후에 여름방학 내내 학교에 나오면서, 무언가 점점 불안감을 느꼈다. 내가 제대로 하고 있다는 생각이 들질 않았다. 그런데 정말 기능대회를 위한 공부를 하기가 너무 싫었다.
말 그대로 방치된 기분이랄까?
집에서도 나를 신경 쓰는 편이 아니었고, 기능반 담당 선생님도 거의 기능반 자체를 없는 것 처럼 취급 하셨다.
생각과는 다르게 또 2학년 2학기도 대충 흘러갔다. 2학년 2학기의 기억은 거의 뿌리채로 없어진 듯한 느낌이다. 그만큼 단조로웠고, 특별할게 없었다고 해야할까?
2학기가 끝날 때 쯤, 이전에도 기능반을 지도해본 경험이 있는 연로한 선생님으로 기능반 담당 선생님이 교체되었다. 지도교사가 달라진다고 해서 나의 생활이 달라지진 않았다. 어차피 이 대회에 대해 자세히 아는 사람은 우리 학교에 없었다. 그래서 나는 반쯤 포기하고 있었다.
2학년 2학기 때 그나마 인상 깊었던 기억이 내가 학교 생활을 하면서 유일하게 선생님에게 목소리를 높여가며 이렇게 관리할꺼면 왜 우리에게 기능반을 제안했으며, 왜 이자리에 앉혀놓은 것이냐고, 이렇게 무관심할꺼면 차라리 기능반을 없애는게 낫지 않겠냐고
말하며 반행했다는 것이다.
처음에는 선생님도 이런 X놈이 어디서 배워먹은 버르장머리야!
라며 화를 내셨지만 그래도 어떻게든 기능반 학생들을 방치하는 수준은 벗어나도록 신경써주셨다.
그렇게 다시 겨울 방학이 시작되었고, 또 집체교육을 갔다. 이 때의 집체교육을 기준으로 나는 빠르게 성장했다.