중학교 3학년인 아들이 숨겨져 있던 자신의 뼈 2개를 찾았다며 자랑스럽게 쇄골을 보여줬다.

살 속에 희미한 쇄골(빗장뼈)의 흔적이 보였다.

턱 선도 V자로 보이기 시작했다고 자랑했다.

출처: 위키

몸무게를 재보라고 하니,

마지막으로 측정했을 때보다 4kg 이상 빠졌다고 자랑했다.

마지막으로 측정했을 때 절망하고는 재보지 않은지 1년 정도 됐고,

키도 많이 컷으니, 실제로는 4kg 이상이 빠진 것 같았다.


지난 몇 년간 태권도, 권투, 헬스 등을 했지만 몸무게는 계속 증가했다.

같이 저녁 마다 달리기도 하고, 상금도 걸어 봤지만 소용없었다.

조금 하다 보면 학원 등의 이유로 빠지기 시작하다

시험 기간이 되면 어느덧 잊혀졌기 때문이다.


살이 빠진 이유는

여름 방학이 시작되면서 놀 시간이 많아지자

친구들과 천변을 자전거로 달리기 시작했기 때문이다.


처음에는 낮에 약 15km 정도 거리를 왕복으로 달리기 시작했다.

얼마나 빨리 가는지를 이야기 하더니, 저녁마다 2시간씩 달리기 시작했다.

비가 온 날도 비가 오지 않으면 나갔고, 학원에서 늦게 온 날도 1시간만 하겠다면 나갔다.


태권도, 권투, 헬스와는 무엇이 달랐을까?


짐작으로는 태권도, 권투도 재미있어 했지만

선생님의 지도하게 배우는 것이라 (체력 단련도 했지만) 품세나 자세 같은 시키는 것 위주로 한 것 같고,

헬스는 달리기 위주로 했다고 하는데,

달리면서 게임 방송만 봤는지 석달 동안 몸무게에 변화가 없어서 중지했다.


친구들과 뭘 하는지 몰라도 아들 말에 따르면 그냥 달린다고 한다.

달리면서 수다떨고, 멈쳐서 수다도 떤다고 했다.

한 친구의 엄마는 그나마 그거라도 하니 좋다며 50만원짜리 자전거를 사줬다고 한다.


뭘하는지 자세하게 알 수 없지만

친구들과 그냥 달리는 것 자체가 즐거울 것이라고 생각한다.

힘이 넘치는 나이(16세)에

그냥 달리는 것도 즐거운 놀이가 될 수 있을 것이다. (설마 분노의 질주를 ...)


그렇게 즐기는 사이 처음으로 살이 빠졌다.


20 여 년전, 제대한뒤 복학하며 자취를 하였고, 매일 자취방으로 친구들이 놀러 왔었다.

S/W 개발과 관련해서 서로 옳다고 참 많이 싸웠고, 그렇게 몇 년을 보냈다.

출처: clipartpanda.com

그러다 각자 직장 생활을 하며 여기 저기 흩어져, 이제는 어디서 무엇을 하는지 모르게 되었지만

친구들과 숱하게 싸우며 경쟁하던 그때 (서로를 맹비난하면서 싸운적도 많았다)

실력이 가장 많이 늘었던 것 같다.


모뎀으로 PC 통신하며 사소한 거라도 먼저 알면 우쭐해 했고,

지기 싫어서 수많은 책을 사서 읽고, 만들어서 자랑했다.

아직도 S/W를 개발하는 것이 즐겁고 큰 문제없이 개발하는 내 실력은

그렇게 친구들과 싸우고 놀면서 시작된 것 같다.



근래에 부산에 자주 가게 되었는데,

버스를 타고 다니다 문득 이상한 느낌이 들었다.

젊은 사람은 보이지 않고 대부분 나이든 사람만이 오고 가고,

길도 왠지 어둡고 좁아 보인다는 생각이 들었다.


부산에 갈 때 마다 살펴 보다,

예전에는 부산역에서 동쪽 위주로 다녀서 몰랐지만

서쪽, 즉 구도심 뒤쪽으로 가니 거의 나이든 사람만이 보이는 것 같았다.


출처: namu1973.tistory.com/508


시골처럼 도시도 노인들만이 사는 지역이 있는 건가라는 생각이 들었다.

인터넷을 찾아봤지만, 없는 것인지 찾지 못한 것인지 헤매다,

통계청 자료가 생각나서 혹시나 하는 마음으로 정리해 봤다.


자료: 통계청(국내통계) : 인구총조사 > 인구부분 > 총조사인구(2016) > 전수부문 > 연령 및 성별 인구 – 시군구 자료


부산의 연령대별(2016년 기준) 데이터를 찾아서

다음과 같이 부산의 지역 구 별로 노인(65세 이상)의 비율을 계산해 봤다.

비율 = 구 연령층 인구 / 구 전체 인구


연령별 인구 비율

연령별 인구


0~29

30~64

65세 이상

0~29

30~64

65세 이상

합계

강서구 33% 56% 11.0%     32,601     56,316     11,036     99,953
금정구 32% 52% 16.0%     77,591   126,911     38,970   243,472
기장군 33% 54% 13.4%     49,405     81,929     20,358   151,692
남구 32% 52% 16.2%     86,918   144,179     44,569   275,666
동구 24% 53% 23.0%     20,403     45,240     19,647     85,290
동래구 30% 55% 15.2%     77,876   143,511     39,580   260,967
부산진구 30% 54% 15.9%   111,138   198,067     58,608   367,813
북구 32% 56% 12.6%     95,933   167,541     38,080   301,554
사상구 31% 56% 13.2%     71,025   128,349     30,275   229,649
사하구 31% 55% 14.1%   101,580   179,237     46,000   326,817
서구 26% 52% 21.3%     28,478     56,684     23,035   108,197
수영구 27% 55% 17.8%     46,571     94,238     30,433   171,242
연제구 29% 55% 15.6%     59,041   110,033     31,221   200,295
영도구 28% 51% 20.9%     34,540     64,193     26,136   124,869
중구 24% 55% 21.5%     10,163     23,391      9,176     42,730
해운대구 31% 55% 13.7%   126,041   222,600     55,459   404,100

동구, 중구, 서구, 영도구의 65세 이상 노인 비율이 20%가 조금 넘는 것으로 계산되었다.

모두 내가 지나갔던 구도심 주변 지역들이었다.


한 국가의 총 인구에서 65세 이상의 인구가

 7% 이상이면 고령화사회,

14% 이상이면 고령사회,

20% 이상이면 초고령사회라고 하고,

우리나라는 고령 사회(14%)로 2026년에 초고령사회(20%)가 된다고 한다.

그런데, 부산의 4개 구는 이미 초고령사회이고 (2016년 자료)

대부분 고령 사회인 것으로 계산되었다.


인구 수로는 해운대구와 부산진구 등이 더 많았지만

비율로는 이 4 개의 구가 더 높았다.

해운대구와 부산진구등은 모든 연령대의 인구 수가 많았다.

(최근에 재개발된 지역이 많다고 하는데, 구가 아닌 동으로 정리하면 재미있는 정보가 있을 것 같다)


혹시나 하는 마음에 서울 자료를 정리해 봤다.

서울은 대부분 14% 미만이었고,

중구, 종로구 등 구도심 지역을 중심으로 65세 이상 인구가 14%를 넘는 것으로 계산되었다.

서울시 평균도 13.3%로 부산의 16.3%보다 아주 적었다.

나는 서울에 살고, 부모님은 부산에 사는 모습과 같았다.


연령별 인구 비율

연령별 인구


0~29

30~64

65세 이상

0~29

30~64

65세 이상

합계

강남구 33% 56% 11.1%   170,605   295,577     58,302   524,484
강동구 31% 57% 12.1%   132,167   240,319     51,037   423,523
강북구 29% 55% 16.5%     89,398   170,930     51,589   311,917
강서구 31% 57% 12.0%   178,176   319,904     67,888   565,968
관악구 33% 54% 13.0%   163,763   263,769     63,650   491,182
광진구 34% 55% 11.4%   116,473   190,413     39,590   346,476
구로구 31% 56% 13.3%   123,430   224,774     53,431   401,635
금천구 30% 57% 13.6%     66,399   126,001     30,390   222,790
노원구 34% 54% 12.4%   185,252   297,311     68,166   550,729
도봉구 30% 55% 14.6%   101,439   183,879     48,847   334,165
동대문구 32% 53% 14.7%   111,183   182,128     50,433   343,744
동작구 33% 54% 13.5%   127,223   209,003     52,438   388,664
마포구 33% 55% 12.4%   118,555   199,925     45,261   363,741
서대문구 34% 52% 14.5%   104,703   159,077     44,639   308,419
서초구 33% 55% 11.5%   136,294   229,205     47,516   413,015
성동구 32% 55% 13.0%     90,411   157,394     36,930   284,735
성북구 34% 53% 13.8%   148,117   230,869     60,637   439,623
송파구 32% 57% 10.9%   200,279   350,679     67,508   618,466
양천구 33% 56% 11.0%   151,694   253,113     49,814   454,621
영등포구 30% 56% 13.8%   105,111   197,035     48,217   350,363
용산구 29% 56% 14.9%     61,693   116,784     31,233   209,710
은평구 30% 55% 14.5%   142,031   257,059     67,723   466,813
종로구 33% 52% 15.4%     47,095     74,736     22,218   144,049
중구 30% 55% 15.3%     35,095     64,760     18,105   117,960
중랑구 30% 56% 13.7%   117,401   222,343     53,803   393,547

내침 김에 광주, 대구, 대전, 인천도 확인해보니

65세 인구 비율이 19%가 넘는 아주 높은 지역이 있었지만

13.2%, 14.7%, 11.9%, 14.5%로 도시 평균이 낮았다.

65세 인구 비율이 높은 지역은 대부분 구도심으로 생각된다.

(비율이 너무 높은 인천의 강화군과 옹진군은 농사짓는 곳으로 봐야 할 것 같은데…)



연령별 인구 비율

연령별 인구



0~29

30~64

65세 이상

0~29

30~64

65세 이상

합계

광주 광산구 40% 52% 8.0%   160,274   211,062     32,485   403,821
남구 33% 52% 14.7%     73,355   113,461     32,289   219,105
동구 31% 49% 19.6%     31,176     49,111     19,540     99,827
북구 35% 52% 12.2%   158,535   234,664     54,749   447,948
서구 35% 54% 11.5%   106,908   164,794     35,453   307,155
대구 남구 27% 53% 19.3%     42,339     82,387     29,858   154,584
달서구 35% 55% 10.6%   202,634   321,795     62,471   586,900
달성군 34% 55% 11.3%     69,599   113,606     23,347   206,552
동구 30% 54% 16.1%   100,781   184,308     54,534   339,623
북구 35% 53% 11.5%   155,655   234,957     50,624   441,236
서구 27% 56% 16.9%     53,208   108,304     32,877   194,389
수성구 34% 53% 12.8%   146,077   229,481     55,233   430,791
중구 29% 52% 19.1%     21,826     40,040     14,569     76,435
대전 대덕구 34% 54% 11.8%     66,042   102,979     22,653   191,674
동구 35% 50% 14.8%     83,326   120,626     35,476   239,428
서구 37% 53% 10.0%   178,937   259,741     48,805   487,483
유성구 41% 52% 7.2%   143,000   184,045     25,258   352,303
중구 32% 53% 15.6%     77,244   129,569     38,343   245,156
인천 강화군 21% 49% 29.7%     13,155     30,232     18,371     61,758
계양구 34% 57% 9.2%   109,307   180,692     29,457   319,456
남구 32% 54% 13.8%   129,289   221,917     56,198   407,404
남동구 33% 56% 10.4%   172,036   288,763     53,510   514,309
동구 28% 54% 17.9%     19,096     37,056     12,217     68,369
부평구 33% 56% 11.3%   173,709   297,290     59,745   530,744
서구 35% 56% 8.6%   172,816   276,261     42,110   491,187
연수구 37% 55% 8.2%   118,907   175,267     26,362   320,536
옹진군 25% 53% 22.0%      4,701      9,879      4,104     18,684
중구 32% 55% 13.6%     34,477     59,409     14,815   108,701

지나던 길들이 너무 어둡고(날씨 탓도 있겠지만) 좁은 느낌 속에

대부분의 동네가

내가 어릴 적(30년전) 살던 모습과 바뀐 것이 없다는 생각이 들었다.

즉, 대부분 일반 주택이 빼곡한 좁은 길이었다.

그래서 부산의 시군구별 주택 종류 자료를 확인했다.



빈도

비율

행정구역별(시군구)

주택

단독주택-일반

아파트

아파트

단독주택-일반

부산광역시

1,174,034

89,663

750,122

64%

8%

   중구

16,590

1,374

5,306

32%

8%

   서구

38,965

8,772

14,458

37%

23%

   동구

30,089

8,433

9,288

31%

28%

   영도구

46,285

8,068

25,441

55%

17%

   부산진구

131,528

12,374

81,825

62%

9%

   동래구

84,217

4,324

53,359

63%

5%

   남구

99,806

9,943

54,940

55%

10%

   북구

104,343

1,087

83,827

80%

1%

   해운대구

139,128

2,799

109,098

78%

2%

   사하구

112,348

6,078

76,369

68%

5%

   금정구

80,635

4,403

43,064

53%

5%

   강서구

33,041

6,408

23,424

71%

19%

   연제구

66,576

4,450

41,632

63%

7%

   수영구

62,169

3,920

33,489

54%

6%

   사상구

74,285

1,571

54,487

73%

2%

   기장군

54,029

5,659

40,115

74%

10%

자료: 통계청(국내통계) : 건설/주택/토지 > 주택 > 주택총조사 > 주택의 종류별 주택 - 시군구

65세 이상 노인의 비율이 20%가 넘은 동구, 중구, 서구, 영도구 아파트 비율은 30%대에 단독 주택은 30% 미만이었다.

(중구는 상업 시설이 많아서 주택 비율이 낮은 듯…)

서울이나 젊은 사람이 많은 지역은 대부분 아파트가 60% 이상이었다.

주택 종류는 단독주택-일반, 단독주택-다가구, 단독주택-영업겸용, 아파트, 연립주택, 다세대주택 등

여러 가지 있지만 이 분야에 대해 관심도 지식도 없기 때문에

단순하게 주택(일반)만 확인해본 결과,

대부분의 노인이 오래된 주택에 살고 있고, 아파트가 많은 지역에 젊은이들이 사는 것 같다.

(주택과 연령의 관계는 좀 더 세밀한 분석을 해봐야 정확할 것 같다.)


간만에 데이터를 들여다 보며

슬픈 마음도 들지만 재미도 있어서 한가지를 더 진행해 봤다.

제공 데는 데이터가 5년 단위(0~4세, 5~9세)로 제공되어

5년후를 예측할 수 있을 것 같다는 생각에 5년후에는 어떻게 되는지 계산해 봤다.


60~64세는 30~64로 묶어서 계산 했는데,

5년 후이니 60~64세를 65세 이상 인구에 넣고

5년간 태어나는 인구를 계산하기 위해 0~4세를 한번 더 더하여 전체 인구를 계산했다.

사망률을 찾아서 계산하는 것이 더 정확하겠지만

굳이 정확한 계산을 필요로 하는 것이 아니라서 생략했다.


2026년에 초고령사회(20%)가 된다고 하는데

부산과 대구는 5년후(2021)에 도시가 초고령사회(20%)가 되고 (다음 표 참조)

부산 동구(31%), 중구(30%)는 30%가 되는 것으로 계산되었다.

(인천은 강화군, 옹진군이 너무 높게 나와 제외)

 

65세 기준

60세 기준 (5 년후)

 

평균

최대

평균

최대

광주 13% 20% 18% 26%
대구 15% 19% 21% 27%
대전 12% 16% 17% 22%
부산 16% 23% 23% 31%
서울 13% 17% 19% 23%
인천 14% 30% 20% 38%


인구나 노인, 주택등에 대하여 아는 것이 없지만

몇 시간에 걸쳐서 이런 저런 계산을 해보니,

특정 지역(구도심)에 나이 많은 사람들이 살고

개발도 되지 않은 채 예전 모습 그대로 산다는 생각이 맞는 것 같다.

시골에 계신 분들이 평생 살던 그 모습 그대로 사는 것처럼

도시에서도 살던 곳에 그렇게 사는 것 같다.


 


검색엔진인 루씬(Lucene)을

쉽게 사용할 수 있도록 만든 오픈 소스 Solr를 쉽게 익히기 위해,

Java에서 Solr를 쉽게 사용할 수 있도록 해주는 SolrJ를 이용하여

검색엔진을 데이터 베이스처럼 사용하여 웹용 게시판을 제작한다.

실제로는 이렇게 사용하지 않지만,

SolrJ의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 게시판을 구현하였다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색

구축 환경

- Solr 6.6.0

- SolrJ 6.6.0

- Eclipse Neon 2 + STS

- Tomcat 8.5, JDK 1.8


게시판을 제작하기 위해 필요한 설치를

다음과 같이 간단하게 정리하고, 상세한 자료는 찾아보길 바란다.

    1. Solr 설치
    2. Eclipse에 STS 설치 및 프로젝트 생성
    3. SolrJ 설치

이상의 설치가 되어 있거나 잘 아는 경우에는

solr에서 core(solrBoard)만 생성해서 실행하고,

gitHub의 코드를 다운 받아서 실행하면 된다.


먼저 Solr를 설치하기 위해 다음 주소에서 다운로드 받는다.

    http://lucene.apache.org/solr/

우측 상단의 [DOWNLOAD] 링크를 선택해서

각자의 운영체제에 맞추어 최신 버전을 다운로드 받는다.

여기서는 윈도우(10)를 기준으로 작성하기 때문에

solr-6.6.0.zip 파일을 다운받아서

그림과 각이 압축을 해제해서 사용하면 된다.

콘솔창(cmd)을 열어서

Solr의 압축 파일을 해제한 경로 하위에 있는 bin 폴더로 이동한 후에,

다음 명령어를 실행해서

Solr 서버를 실행하고 (start), solrBoard라는 코어(core)를 생성한다.

코어는 DBMS의 테이블(Table)과 비슷한 개념으로,

solrBoard 라는 데이터 저장 공간을 생성한 것이다.

> solr start  (Solr시작)

> solr create -c solrBoard  (코어생성)

> solr restart -p 8983 (Solr 재시작)


웹 브라우저에서 Solr 관리자 페이지에 접속하여,

그림과 같이 solrBoard가 생성되어 있으면

Solr 설치와 데이터를 저장할 코어 생성이 완료 된 것이다.

http://localhost:8983/solr

위 그림의 중앙 위쪽에 빨간색 박스안에 어떤 메시지가 있다면

오류가 발생한 것이니 수정하고 Solr를 재시작해야 한다.


solr에서 한글을 사용할 수 있도록 설정한다.

solr를 설치한 경로 하위에서 managed-schema 파일을 찾아서 수정한다.

코어를 solrBoard로 생성했기 때문에

solrBoard란 폴더가 다음과 같이 생기고 설정 폴더(conf)에서 찾을 수 있다.

solr-6.6.0\server\solr\solrBoard\conf


위 그림에서 _text_와 관련된 두 줄을 삭제하고

다음 코드를 적당한 위치(120 라인 앞)에 추가한다.


<dynamicField name="*_txt_ko" type="text_ko" indexed="true" stored="true"/>
<fieldType name="text_ko" class="solr.TextField">
    <analyzer type="index">
        <tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.ClassicFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" bigrammable="false"/>
        <filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory"/>
        <filter class="solr.StopFilterFactory" words="lang/stopwords_ko.txt" ignoreCase="true"/>        
        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
    </analyzer>

    <analyzer type="query">
        <tokenizer class="org.apache.lucene.analysis.ko.KoreanTokenizerFactory"/>
        <filter class="solr.LowerCaseFilterFactory"/>
        <filter class="solr.ClassicFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.KoreanFilterFactory" hasOrigin="true" hasCNoun="true" bigrammable="true"/>
        <filter class="org.apache.lucene.analysis.ko.WordSegmentFilterFactory" hasOrijin="true"/>
        <filter class="org.apache.lucene.analysis.ko.HanjaMappingFilterFactory"/>
        <filter class="org.apache.lucene.analysis.ko.PunctuationDelimitFilterFactory"/>
        <filter class="solr.StopFilterFactory" words="lang/stopwords_ko.txt" ignoreCase="true"/>
        <filter class="solr.KeywordMarkerFilterFactory" protected="protwords.txt"/>
        <filter class="solr.SynonymFilterFactory" synonyms="synonyms.txt" ignoreCase="true" expand="true"/>
    </analyzer>
</fieldType>


검색엔진을 제대로 구축하는 것이 아니기 때문에,

위 코드에서 지정된 불용어(stopwords_ko.txt)파일은

인터넷에서 다운로드받아서 지정된 폴더에 추가하거나

위 코드에서 해당 행을 제거하고 사용해도 된다.


다음으로 managed-schema 파일에서

다음과 같이 게시판의 필드를 추가한다.

  <field name="brdwriter" type="text_ko" multiValued="true" indexed="true" required="true" stored="true"/>
  <field name="brdtitle" type="text_ko" multiValued="true" indexed="true" required="true" stored="true"/>
  <field name="brdmemo" type="text_ko" multiValued="true" indexed="true" required="true" stored="true"/>
  <field name="brddate" type="string" multiValued="false" indexed="true" required="true" stored="true"/>


루씬 한글 형태소 분석기인 arirang analyzer (arirang.lucene-analyzer-x.x.jar, arirang-morph-1.0.x.jar)을

루씬 커뮤니티에서 다운받아서

Solr를 설치한 웹 폴더 (\solr-6.0.0\server\solr-webapp\webapp\WEB-INF\lib)에 복사한다.


이상의 설정이 적용되도록 Solr 서버를 재 가동한다.

solr restart -p 8983


이상의 설정들은 한글을 인텍싱하고 조회하는 설정을 처리한 것으로

이상의 내용들을 생략하고, Solr만 실행해도 테스트를 진행 할 수 있다.

게시판 예제는 쉽게 Solr를 실행하고 테스트 해 볼 수 있도록

한글 처리와 필드 정의 없이 사용하도록 제작되었다.


이상으로 Solr 설정을 간단하게 정리하였다.

자세한 설치와 설명은 검색을 하거나 여기를 참고하면 된다.


주의 1: 필드 정의에서 multiValued를 true로 지정했다. 필드 지정을 하지 않으면 기본값이 true 이기 때문이고, 따라서 게시판 예제에서 brdtitle[0]}, brdwriter[0]등과 같이 사용한다. false이면 brdtitle, brdwriter등으로 사용한다.

주의 2: 필드 정의에서 brddate를 문자열로 정의하였다. 필드 지정을 하지 않으면 tdates로 지정된다. 게시판 예제는 필드 지정없이 사용하는 것을 기본으로 하기 때문에 위와 같이 문자열로 지정한 경우 jsp 파일에서 formatDate를 사용한 것을 모두 다음과 같이 수정하여야 한다.

string으로 지정한 경우: <fmt:formatDate pattern = "yyyy-MM-dd" value = "${listview.brddate[0]}" />

tdates로 지정한 경우: <c:out value="${listview.brddate}"/>


다음으로 Eclipse에 STS (Spring Tool Suite)를 설치하고,

새로운 프로젝트(solrBoard)를 생성한다.


Eclipse의 Help > Eclipse Marketplace 메뉴에서 STS를 찾아서 설치한다.

File > New > Other 메뉴를 실행하고,

그림과 같이 Spring > Spring Legacy Project를 선택해서

새로운 Spring 프로젝트를 생성한다.


프로젝트 이름을 solrBoard로 입력하고,

템플릿을 Spring MVC Project로 지정해서 다음으로 넘어간다.

적당한 패키지 이름을 지정한다.

solrBoard로 프로젝트가 생성되었으면

Tomcat 서버에 등록해서 실행한다.

웹 브라우저에서

다음과 같이 입력하여 그림과 같이 실행되면

Spring 기반의 게시판을 제작할 준비가 된 것이다.

http://localhost:8080/solrBoard/

톰캣 실행시 많은 문제가 발생 할 수 있다.

Java 버전이 안 맞거나,

Tools.jar 파일이 없다거나,

Maven이 제대로 라이브러리를 설치하지 못하는 등의 문제가 발생할 수 있다.


실행은 되었지만 한글이 깨어지는 경우

solrBoard\src\main\webapp\WEB-INF\views 폴더에 있는

home.jsp의 첫 행에 다음 문장을 추가하면 된다.

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%>


마지막으로 SolrJ를 설치한다.

다음 웹 사이트(Maven Repository)에 접속해서

설치할 SolrJ의 버전을 선택한다.

https://mvnrepository.com/artifact/org.apache.solr/solr-solrj

여기서는 현재 최신 버전인 6.6.0을 선택한다.


Maven 탭의 내용을 복사한다.



SolrJ를 사용할 프로젝트(solrBoard)의 pom.xml 파일을 열어서

적당한 위치에 붙여넣기하고 저장한다.

저장만 해도 SolrJ가 설치가 되지만

제대로 설치되지 않으면

solrBoard 프로젝트를 선택하고

컨택스트 메뉴에서 Run As > Maven Install로 설치하면 된다.







'서버 > 검색엔진' 카테고리의 다른 글

3. Solr 예제 분석 - 스키마(Schema)  (0) 2020.01.12
4. Solr 예제 분석 - DIH  (0) 2020.01.12
2. SolrJ 사용법  (0) 2017.08.24
3. Solr 단순 게시판 (CRUD)  (0) 2017.08.24
4. Solr 단순 게시판 - 페이징과 검색  (0) 2017.08.24

Solr의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 게시판을 구현하였다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색


Solr등 각종 설치와 관련된 상세한 내용은 이전 글을 참고하고,

여기서는 간단하게 Solr를 다운로드 받은 후

다음과 같이 실행하여 Solr 서버의 설정을 간단하게 마친다.

그리고, github에서 게시판 소스를 받아서 예제를 실행하면 된다.

> solr start
> solr crate -c sqlBoard
> solr restart -p 8983


게시판과 관련된 설명은

기존에 Spring 4 + MyBatis 3 + MariaDB로 개발했던 게시판의 내용을 읽어 보길 바라고

여기서는 게시판에 대한 설명보다는 SolrJ의 사용법을 중심으로 정리한다.


SolrJ는 Java에서 Solr 서버를 제어할 수 있도로 다양한 기능을 제공하는 클래스로,

사용법은 아주 간단하고, Solr 문서 사이트에 쉽게(?) 설명되어 있다.

이 내용을 토대로 SolrJ의 몇 가지 기능을 정리하였다.


먼저, Java에서 SolrJ를 이용하여 Solr 서버에 접속하는 코드이다.

String urlString = "http://localhost:8983/solr/solrBoard";

SolrClient solr = new HttpSolrClient.Builder(urlString).build();

HttpSolrClient 클래스를 이용하여 접속할 Solr 서버의 주소를 지정한다.

다만, 하나의 Solr 서버에는 많은 코어(core)가 있을 수 있기 때문에

사용하고자 하는 코어(solrBoard)를 지정해야 한다.


다음으로, Solr 서버에서 데이터를 조회하는(Query) 코드이다.

        SolrQuery query = new SolrQuery();
        query.setQuery("*:*");
        query.setRows(Integer.MAX_VALUE);
        query.addSort("id", ORDER.desc);

        QueryResponse rsp = solr.query(query);
        SolrDocumentList docs=rsp.getResults(); 
        for(int i=0;i<docs.getNumFound();i++){
            System.out.println(docs.get(i));
        }

SolrQuery클래스를 이용하여

조회(Query)할 데이터의 조건을 설정하고 solr.query로 실행한다.

solr 변수는 Solr 서버에 접속하면서 생성한 변수이다.


조회할 데이터의 조건은

루씬(Lucene) 문법으로 여기서는 자세하게 정리하지 않는다.


코드에 사용된 조건은

모든 데이터(*.*)를 

페이징 처리 없이 모두 가지고 와서(Integer.MAX_VALUE) 출력한다.

데이터 정렬(sort)은 id 필드의 값이 큰 순서(Descending)로 하였다.


조건에 맞추어 반환된 데이터(SolrDocumentList)는 Json 형식의 배열로 반환되고

getNumFound() 함수로 총 데이터의 개수를 알 수 있다.


데이터를 추가(수정)하는 방법은

다음 코드와 같이 SolrInputDocument를 이용한다.

        SolrInputDocument solrDoc = new SolrInputDocument();
        solrDoc.addField("id", 1);
        solrDoc.addField("title", "단순게시판");
        
        solr.add(solrDoc);
        solr.commit();

SolrInputDocument로 생성한 solrDoc변수에

필드명과 저장할 값을 지정해서 addField() 함수로 추가하면 된다.

필드명이 Solr 서버(실제로는 코어, managed-schema)에 있을 경우

지정된 데이터 형태로 저장되고,

필드명이 없을 경우 Solr 서버가 판단하여 적정한 데이터 형태로

지정해서 필드를 생성한 뒤 저장한다.


Solr에서 core를 생성할 때,

기본 설정으로 id 필드를 키 필드(Primary key, Unique Field)로 한다.

따라서, 하나의 글을 저장할 때,

이 키 필드(id)의 값이 있으면 수정, 없으면 신규로 저장한다.

즉, DBMS의 merge 문과 같은 방식으로 사용한다.


마지막으로 삭제는 다음과 같이

삭제할 필드명과 조건 값을 지정하면 된다.

        solr.deleteByQuery("id:1");
        solr.commit();


데이터를 추가하거나 삭제한 경우

항상 commit()을 호출하여 데이터 변경이 반영되도록 한다.




Solr의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 단순 게시판을 구현하였고,

소스는 Github에서 받을 수 있다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색


게시판을 만들기 위해서는

MariaDB 와 같은 DBMS에서 데이터를 저장하는

다음과 같이 테이블(TBL_BOARD)을 생성해야 한다.

CREATE TABLE TBL_BOARD (

    BRDNO int(11) NOT NULL AUTO_INCREMENT,         -- 글 번호

    BRDTITLE varchar(255),                                -- 제목

    BRDWRITER varchar(20),                              -- 작성자

    BRDMEMO   varchar(4000),                           -- 내용

    BRDDATE     datetime,                                -- 작성일자

    PRIMARY KEY (BRDNO)
) ;

기본적인 게시판을 구현하기 위해서는 이상의 5 개의 필드가 필요하고,

Solr에서 정의하는 방법은 설정 부분에서 정리하였다.

여기에서는 이러한 설정을 하지 않고 코어(solrBoard)만 생성해서 진행한다.

데이터를 저장할 때 Solr에서 자동으로 생성되는 기능을 이용하였다.

입력 값에 따라서 다음과 같이 자동으로 생성된다.

  <field name="brdtitle" type="strings"/>
  <field name="brdwriter" type="strings"/>
  <field name="brdmemo" type="strings"/>
  <field name="brddate" type="tdates"/>

대부분 위에서 정의한 필드 구조로 생성되지만

글 번호(BRDNO)는 키 필드(PRIMARY KEY)로

데이터 값을 유일하게(Unique) 유지하는 역할을 하는데 Solr에서 별도로 설정을 해야 한다.

여기에서는 설정을 생략하기 위해 코어 생성시 기본 지정되는 ID 필드를 이용한다.

HTML이나 VO 클래스에서는 BRDNO로 사용하고

SolrJ를 이용하여 저장하거나 조회 때에는 ID라는 이름으로 사용한다.



게시판의 기능 중 글 리스트를 먼저 구현한다.

Java에서 board1List 컨트롤을 다음과 같이 작성한다.

Board1Ctr.java

이상의 코드는 모두 SolrJ 사용법 중 데이터 조회 부분에 정리되어 있다.

여기에서는 기본 예제로

Solr 서버에 있는 모든 데이터를 가지고 와서 출력하도록 했기 때문에

설명에 사용된 코드를 그대로 사용하였다.

차이는 ModelMap을 이용하여

Solr 서버에서 가지고온 데이터(docs)를

listview라는 이름으로 Jsp로 넘겨 주는 것이다 [라인 10].


/board1/boardForm.jsp

대부분의 코드는 디자인을 위한 HTML 코드이고

주어진 데이터를 화면에 출력하는 코드는 18 라인부터 29라인이다.

주어진 데이터(listview)의 개수 만큼  반복(forEach)해서

각 행(tr)을 생성하여 출력한다.


listview는 ModelMap으로 컨트롤(Java)에서 주어진 데이터 이고

Jsp(HTML)에서는 [listview.변수]로 사용하면 된다 [라인 24~27].


다만 키인 id를 제외하고

나머지 필드는 모두 배열[]로 처리되어 있다.

필드 정의에서 multiValued가 true로 지정되었기 때문으로 [설치 참조]

별도로 지정하지 않으면 true가 기본값으로 사용된다.


작성일자(brddate)에 형식(formatDate) 태그를 사용한 것은 [라인 27]

Solr 서버가 주어진 데이터(yyy-mm-dd)를 날짜로 인식해서

tdates 형으로 지정해서 저장하기 때문이다.

tdates 형으로 지정하면 [Sat Aug 19 09:00:00 KST 2017]로 출력한다.

작성일자(brddate)를 managed-schema에서 string으로 지정하는 것이 좋지만

여기서는 형식(formatDate) 태그를 이용하여 처리하였다.


주의: 게시판 예제는 필드 정의를 하지 않았다는(solr만 시작) 전제로 작성되었다.

만약, 설치에서 정리한 데로 필드를 정의하였다면,

brddate를 날짜형(tdates )이 아닌 문자열(String)로 지정되어 있기 때문에

27 라인에서 오류가 발생한다.

27 라인을 다음과 같이 수정하여야 한다.

<td><c:out value="${listview.brddate}"/></td>

필드 정의 부분의 주의 사항을 읽어보길 바란다.


톰캣을 실행하고

웹 브라우저에서 다음과 같이 입력해서 실행을 확인한다.

http://localhost:8080/solrBoard/board1List


이번에는 글 쓰기 기능을 구현한다.

게시판 글 쓰기는 글을 작성하기 위한 폼(board1Form)과

작성된 내용을 저장하는 부분(board1Save)으로 구성된다.


Board1Ctr.java

여기에서는 글쓰기와 글 수정을 하나로 구성했기 때문에

조금 복잡하게 보이지만 단순한 개념이다.

글 쓰기 폼은 아무 것도 하지 않고 입력 받을 HTML을 화면에 출력한다.

글 수정 폼은 지정된 글 번호(brdno)에 해당하는 내용을 찾아서 (getOneboard) [라인 5]

입력 상자에 초기값으로 넣어준다.


Solr에서 데이터를 가지고 오는 것은

글 읽기와 동일하기 때문에 getOneboard() 함수로 작성하였다.

상세한 내용은 글 읽기에서 정리하였으니 넘어간다.

/board1/boardForm.jsp

글 내용을 작성한 뒤에

글 저장(board1Save)을 호출하기 위해

Form 태그에서 action으로 지정하였다 [라인 1].


컨트롤의 getOneboard()함수에서

지정된 글에 대한 정보를 boardInfo에 넣어서 반환했기 때문에

Jsp에서도 [boardInfo.변수]로 사용한다 [라인 11, 15, 19, 24, 25].


두 개의 HTML HIDDEN 태그가 사용하였다 [라인 21, 22].

글 번호(brdno)는 글쓰기 일때는 값이 없고

글 수정일때는 값이 있게 되어,

저장시(board1Save ) 신규 등록과 수정의 기준이 된다.


작성일자(brddate)를 HIDDEN 태그로 가지고 있는 것은 Solr의 특성때문이다.

글 내용을 저장할 때 지정된 필드만 수정하는 것이 아니고

행 자체를 수정하기 때문에

수정되지 않았어도 같이 필드 값을 주지 않으면

빠진 필드는 빈 값으로 채워진다.

따라서 입력 받지 않는 작성일자도 값을 가지고 있다가 [라인 22]

같이 board1Save 컨트롤로 넘겨 준다.

작성일자는 Solr에서 [Sat Aug 19 09:00:00 KST 2017]으로 반환되기 때문에

yyyy-mm-dd로 변환해서 가지고 있는다 [라인 22].


웹 브라우저에서 다음 주소를 입력하여 실행을 확인 할 수 있다.

http://localhost:8080/solrBoard/board1Form



[저장]을 선택하면 오류가 날 뿐 저장되지 않는다.

Solr 서버에 데이터를 저장하는 컨트롤(board1Save)을 작성해야 한다.

다음 코드 중 핵심 코드는 SolrJ 사용법에서 정리하였다.

Board1Ctr.java

Solr 서버에 데이터를 저장하기 위해

SolrJ로 SolrInputDocument 클래스를 사용하여 저장한다 [라인 9~18].


사용자가 입력한 값을 boardInfo로 받아서 [라인 2]

SolrInputDocument 에 넣어준다[라인 9~14].

다만, 키 필드를 id로 지정했기 때문에 글 번호(brdno)를 id로 지정하였다 [라인 10].


이상의 코드는 SolrJ 사용법에서 정리한 내용과 동일하고

차이나는 부분은 글 번호(brdno)와 작성일자(brddate)의 값을 생성하는 부분이다.

이 두 필드는 사용자가 입력하는 값이 아니고

프로그램에서 생성하는 값이기 때문이다.


먼저, 작성일자는 SimpleDateFormat 클래스를 사용하여

현재 서버의 시간을 yyyy-MM-dd형식으로 저장하도록 했다.


글번호는 조금의 트릭을 사용하였다.

글번호는 데이터 저장 공간(테이블) 내에서 유일한 값(Primary key, Unique)을 가져야 한다.

DBMS에서는 최대 글번호에 1씩 증가하여 유일한 값을 유지한다.

Solr에는 이러한 기능이 없기(?) 때문에 날짜를 이용하여 생성하였다 [라인 30].

서버의 시간을 밀리센컨드로 받아서 [라인 31]

1~10까지의 숫자 중 아무 숫자랑(random) 결합하여 유일한 값을 생성하였다 [라인 32].

서버 성능에 따라 중복 값이 발생할 수 있지만 테스트 용으로는 무난하게 사용할 수 있다.


다음으로 글 읽기(baord1Read) 기능을 구현한다.

Board1Ctr.java

글읽기는 글 수정과 같이

주어진 글 번호에 대한 상세 정보를 조회하여 화면에 출력한다.

따라서 getOneboard() 함수를 작성하고

이 함수를 각각의 컨트롤에서 호출하여 구현하였다.


주어진 글 번호에 대한 상세 정보를 조회하는 코드는

데이터를 찾는 것이기 때문에 글 리스트와 동일하다

차이점은 주어진 글 번호를 조건으로 한다는 점이다.

(실제로는 글 리스트는 글 본문을 조회하지 않고, 글읽기는 조회한다는 차이도 있다)


글 번호를 조건(Query)으로 하기 위해

setQuery 함수의 파라미터에 조건("id:" + brdno)을 지정하였다 [라인 11].

글 리스트에서는 조건을 전체 데이터("*:*")로 지정하였다.


조회 결과 반환된 데이터(docs)가 [라인 15]

1개 이상을 반환할 수 있기 때문에 배열로 반환된다.

글 읽기는 하나의 데이터(행)만 있으면 되기 때문에,

Jsp로 해당 글 정보를 넘기기 전에 docs의 get() 함수를 이용하여

첫(0) 번째 데이터를 boardInfo라는 이름으로 지정하였다 [라인 19].


/board1/boardForm.jsp

글 읽기 화면(jsp)에서는 컨트롤에서 주어진 값들을(boardInfo) 출력한다.

글 쓰기 폼과 유사하게 작성하고,

디자인 구성이나 입력받기 위한 태그(text, textarea)를 사용하는 차이가 있다.



마지막으로 글 삭제(baord1Delete) 기능을 구현한다.


Board1Ctr.java

글 삭제는 코드와 같이 매우 간단하다.

deleteByQuery() 함수를 이용하여

삭제할 때 조건으로 사용할 필드와 값을 지정하면 된다.


글 읽기에서 삭제 버튼을 눌러서 잘 실행되는지 확인한다.


이것으로 Solr를 이용한 단순 게시판을 구현하였다.

웹 브라우저에서 다음 주소를 입력하여 실행을 확인 할 수 있다.

http://localhost:8080/solrBoard/board1List




이상의 내용을 Solr의 입장에서 정리하면

게시판 글 쓰기(수정)와 삭제는 색인 작업(Indexing)이 되고,

글 리스트와 글 읽기는 검색 작업(Query)이 된다.

즉, 검색 엔진의 가장 중요한 기능은 색인과 검색으로

여기에서는 SolrJ로 Solr의 색인과 검색을 제어하는 간단한 방법들을 정리하였다.




SolrJ의 사용법을 익히기 위해

Spring + SolrJ + Solr (Lucene) 기반으로 단순 게시판을 구현하였고,

소스는 Github에서 받을 수 있다.

1. Solr로 만드는 단순 게시판: 각종 설치

2. SolrJ 사용법

3. Solr 단순 게시판 (CRUD)

4. Solr 페이징과 검색


이전 예제는 Solr 서버에 대한 데이터 입출력(CRUD)을 익히기 위한 예제로

간단한 단순 게시판을 구현하였고,

여기에서는 검색 엔진인 Solr 서버의 기능을 조금 더 익히기 위해

페이징과 검색 기능을 구현한다.


페이징과 검색은 게시판 예제 중 제법 고급 기능으로

게시판에 대한 조금은 깊은 이해가 필요하다.

다만 이 글은 Solr에 대한 사용법을 익히는 것이 목적이기 때문에

게시판과 관련된 설명은 간단하게 정리하니

Java로 구현된 게시판의 정리 내용을 읽어보면 도움이 될 것이다.


먼저 페이징(Paging) 기능을 구현한다.

페이징은 다음 그림의 하단에서 보듯이 주어진 데이터를 나누어서 출력하는 것을 의미한다.

많은 데이터를 하나의 웹 페이지 출력하면 시간과 메모리 등의 문제가 발생히 때문에,

지정된 개수(10, 20개 등) 만큼 조회해서 출력한다.

페이징 처리에 대한 구체적인 구현 방법은 제법 복잡해서 여기에서는 정리하지 않고,

두 가지 기본 개념만 간단하게 정리하면

주어진 데이터의 개수를 출력할 데이터 개수(10, 20개 등)로 나누어

전체 페이지 수를 계산해서 화면에 출력한다.

예로, 데이터가 100 개면 10 개의 페이지가 출력된다.


현재 페이지가 지정되면 (리스트에서 사용자가 클릭하면)

계산을 해서 조회할 데이터의 시작 번호를 찾게 되고

이 번호 이후의 데이터를 지정한 개수만큼 가져와 출력한다.


이외에 상세한 정보는 Java(Spring) 게시판 예제의 정리를 참고하면 된다.


Board2Ctr.java

기존에 사용한 코드 (board1List)에 몇 가지를 추가하여 구현하였다.

추가한 코드는 데이터의 개수를 세어 [라인 9~12]

페이지를 계산하고 [라인 13],

시작 데이터와 조회할 데이터 개수를 지정하는 코드이다 [라인 16, 17].


개수를 세는 조회(Query)에는 데이터 반환이 필요 없기 때문에

반환 할 데이터 개수를 0 으로 지정한다 (setRows(0)) [라인 9].

총 데이터의 개수(getNumFound)만 확인하여 [라인 12],

PageVO의 pageCalculate() 함수에 파라미터로 제공하여 계산한다 [라인 13].

pageCalculate() 함수와 페이징에 대한 상세한 설명은

Java(Spring) 게시판 예제의 정리를 참고 하면 된다.


이 함수를 실행하고 나면

PageVO 클래스의 rowStart에 시작번호가 지정되어 있기 때문에

getRowStart() 함수로 가져와서 -1 하였다 [라인 16].

이 클래스는 자바 게시판 예제에서 작성한 것으로

애초 개발이 Oracle 기반으로 제작되었고,

Oracle에서는 데이터 시작 번호를 1 부터 한다.

Solr 에서는 0 부터 시작하기 때문에 PageVO를 수정하지 않고 -1로 구현하였다.


setRows() 함수로 가져올 데이터 개수를 지정하였다 [라인 17].


이렇게 실행된 데이터(docs)와 페이지 정보(pageVO)를

Jsp로 넘겨서 화면에 출력한다 [라인 21, 22].

/board2/boardList.jsp


화면 출력은 페이지 번호를 출력하는 forEach 문만 이해하면 된다 [라인 3].

forEach문은 앞서 글 리스트의 화면(boardList.jsp)에서 사용하는 것처럼

지정된 데이터(배열)의 개수만큼 반복처리하는 것이 있고,

여기에서 사용한 것처럼 시작 값(pageStart)부터 종료 값(pageEnd)까지 반복하는 기능이 있다.


데이터가 10,000개이고 10개씩 출력할 경우,

페이지가 1,000개가 되기 때문에 모두 출력할 수 없어

페이지 번호(시작페이지와 종료페이지)에 대하여 페이징을 하게 된다.

PageVO 클래스에 있는 pageCalculate()함수에서

시작페이지(pageStart)부터 종료페이지(pageEnd)를 계산해 준다.

이 값을 하나씩 i 변수에 넣어서 페이지 번호를 출력한다 [라인 3].


부가적으로 새로운 글 번호를 출력하도록 했다.

앞서의 리스트 그림을 보면

날짜와 시간으로 된 긴 글 번호(brdno) 값 대신에

11 부터 2 까지의 새로운 번호가 출력된 것을 볼 수 있다.

내부적으로는 날짜와 시간으로 된 글 번호(brdno) 값으로 처리하지만

사용자에게 보여주는 값은 정리된 일련번호를 보여주는 것이다.

다음 코드를 사용해 보고 계산식은 각자 확인해 보길 바란다.

<c:forEach var="listview" items="${listview}" varStatus="status">   
    <tr>
        <td><c:out value="${pageVO.totRow-((pageVO.page-1)*pageVO.displayRowCount + status.index)}"/></td>
        <td><a href="${link}"><c:out value="${listview.brdtitle[0]}"/></a></td>
        <td><c:out value="${listview.brdwriter[0]}"/></td>
        <td><fmt:formatDate pattern = "yyyy-MM-dd" value = "${listview.brddate[0]}" /></td>
    </tr>
</c:forEach>

/board2/boardList.jsp


이번에는 실제 검색에서 사용되는 기능과 유사한 게시판의 검색 기능을 구현한다.

실제 검색은 더 많은 필드들을 복잡한 조건으로 구현하고,

여기서는 다음 그림의 아랫 부분에 있는 것처럼

게시판의 제목과 내용 필드에 대하여

검색하려고 하는 값(검색 키워드)을 이용하여 조회(Query)하는 기능을 구현한다.

구현 방법은 글 읽기에서 작성했던 것처럼

Solr의 setQuery() 함수에 조건을 지정하여 구현한다.

글 읽기에서는 검색할 필드와 값을 “:”으로 구분하여 지정하였다.

예: 글번호:1      =>     id:1


여기에서는 사용자가 선택한 필드와 값(검색키워드)를 이용하여

다음과 같은 문장을 생성하여 조건으로 사용하면 된다.

제목:검색키워드 AND 내용:검색키워드

=>   brdtitle:HTML AND brdmemo:HTML


/board3/boardList.jsp

이해를 위해 화면 처리 부분을 먼저 정리한다.

검색 결과에 대해서도 페이징 처리를 구현해야 하기 때문에

검색 필드(checkbox)와 키워드(text), 현재 페이지 번호(hidden)를 가지고 다녀야 한다.

이 각각의 값을 Get방식으로 전달하기 보다는

Post 방식이 더 쉽기 때문에 Form 태그를 사용하였고, [라인 1].

코드를 줄여서 보이기 위해, 앞서 구현한 페이징을 별도의 파일로 처리하였다 [라인 2].


HTML에서 두 개의 체크 박스를 같은 이름(searchType)으로 지정하면

Java에서 콤마(,)로 받게 된다.

첫 번째 체크 박스는 brdtitle [라인 5],

두 번째 체크 박스는 brdmemo [라인 7]

로 값(Value 속성)을 지정하고,

사용자가 둘다 선택하면 [brdtitle, brdmemo]로 Java에 전달된다.

이 값을 searchType 변수로 받아서

다시 화면에 출력할 때 선택된 상태로 보일 때 사용한다.

즉, searchType 변수 값에 brdtitle 문자열이 있으면(indexOf>-1)

해당 체크 박스가 체크 표시(checked)가 되도록 한다 [라인 5].

글 내용(brdmemo)도 동일하게 구현한다 [라인 7].


화면 처리와 관련하여 보다 상세한 설명은 Java 게시판 예제의 정리를 참고하면 된다.


Board3Ctr.java

컨트롤에서는 Solr 서버에서 원하는 데이터를 가지고 올 수 있도록 검색 조건을 생성한다.

4 라인부터15 라인까지의 내용이 조건을 위해 추가한 코드이다.


사용자가 체크박스에서 선택한 내용을 searchType으로 받아서 [라인 4]

콤마(,)로 분류하여 각 선택 값을 배열로 변환한다 [라인 7].

이 배열의 개수만큼 반복해서 [라인 8]

필드명과 검색 키워드(searchKeyword)를 콜론(:)으로 구분하여 조건을 생성한다.

조건과 조건은 AND로 묶었다 [라인 9]

(OR로 처리해도 된다. 개념상 OR가 더 적당하다)


이렇게 생성한 조건(queryStr)을

글읽기에서 사용한 것처럼 setQuery로 지정하면 된다 [라인 17].


주의 : 코어만 생성하고 Solr를 실행한 경우 한국어 검색은 제대로 실행 되지 않을 수 있다.

형태소 분석기 등을 추가하고, 필드 설정을 제대로 해야 한다.

자세한 내용은 설정 부분을 참고하면 된다.


이상으로 Solr와 SolrJ의 사용법을 익히기 위해

Spring 4 + MyBatis 3 + MariaDB로 제작한 게시판 예제 중 몇 가지를

MariaDB 대신에 Solr 서버를 데이터 베이스처럼 사용해서 구현했다.

게시판과 관련된 설명들은 자세하게 정리하지 않았으니

기존 게시판 예제의 정리를 읽어보길 바란다.


나머지 예제들 (총 8 가지의 게시판이 있음)도 구현해보면

Solr에 대한 이해를 높일 수 있고,

프로그래밍 실력을 향상시키는데 도움이 될 것이다.



'서버 > 검색엔진' 카테고리의 다른 글

3. Solr 예제 분석 - 스키마(Schema)  (0) 2020.01.12
4. Solr 예제 분석 - DIH  (0) 2020.01.12
1. Solr로 만드는 단순 게시판: 각종 설치  (18) 2017.08.24
2. SolrJ 사용법  (0) 2017.08.24
3. Solr 단순 게시판 (CRUD)  (0) 2017.08.24

nineBatis (9batis)

Java에서 SQL문을 처리 할 때 많이 사용하는 Mybatis처럼,

Node.js에서 SQL을 쉽게 사용하기 위해 간단하게 제작한 라이브러리이다.


여기에서는 이전에 정리한 Node.js 기반의 단순 게시판

9batis를 적용하여

9batis의 사용법을 예제와 문서로 정리하였다.


게시판 예제는 Node.js (express)와 MySQL을 기반으로

단순 CRUD 기능을 구현한 예제이고,

board1.js, board2.js의 2가지 예제로 구성되어 있다.


이 예제에 9batis을 적용하여 3 가지 예제를 추가하였다.

9batis를 이용하여

SQL 문장만 반환 받아서 사용하는 단순 예제(board3.js),

SQL을 실행하고 결과를 반환 받아서 사용하는 기본 예제(board4.js),

중첩 SQL문 실행 등 다소 복잡한 기능을 정리한 예제(board5.js)로 구성하였다.


routes 폴더에 있는 각 컨트롤(board3.js, board4.js, board5.js)들은

각각의 HTML 파일(ejs)을

views 폴더 하위에

컨트롤과 동일한 이름의 폴더(board3, board4, board5)로 가지고 있다.

각 폴더에는 list.ejs, form.ejs, read.ejs 파일이 있다 [그림 왼쪽 참조].


이 샘플 코드는 github에서 받을 수 있다.

github 사용법은 게시판 예제에 정리되어 있으니 참고하면 되고

정리된 내용 중에서 다운 받을 github 주소를

boardJS에서 nineBatisSample로 바꾸어서 따라하면 된다.

https://github.com/gujc71/nineBatisSample


이제부터 nineBatis (9batis)의 사용법에 대해서 정리한다.


Node.js에서는 SQL문을 문자열로 처리하여

컨트롤과 같이 작성한다.

nineBatis (9batis)에서는 SQL문을 별도의 XML 파일로 작성하여 관리한다.

다음 코드(board3.xml)와 같이

select, insert 등의 태그를 사용하여 SQL 문을 작성하면 된다.

다음 코드는 게시판 예제(board1.js)에서 SQL 문만 따로 추출하여

9batis 문법에 맞추어 XML로 정리한 것이다.

board3.xml

XML 파일은 한개 이상을 사용할 수 있고

별도의 폴더에 (예제는 routes/sql) 모아서 사용해야 한다.


태그에 사용되는 id (selectBoardList, selectBoardOne 등)는

전체 XML 파일내에서 고유한 값이어야 한다.

중복된 id를 지정하면 Node.js 시작시 오류 메시지를 출력하고

Node.js 실행을 중지한다.


XML 파일에서 제공되는 기본 기능(명령어)은 4 가지이다.

숫자 변수, 문자 변수, include, if문이 지원된다.

이것은 Mybatis와 비슷하게 구현했는데

사용한 라이브러리의 제약(xml2js)등의 문제로 아직은 사용법이 다소 부족하다.


위 코드에서 사용한 것처럼

숫자 변수는 ${변수} [라인 4],

문자 변수는 #{변수} 로 사용한다 [라인 21, 27].

MyBatis에서는 #을 PrepareStatement로

$를 Statement로 정의하여 구분하지만,

여기서는 쉽게 정의하기 위해 문자와 숫자 변수로 정의하였다.


include, if문은 XML로 작성해야 하는데

xml2js 파싱시 처리가 되지 않아

{include ref='sql 태그 id'}로 사용한다 [라인 16, 22, 32].

include문은 위 SQL 문처럼 (includeWhere)

중복된 코드(sql 태그)를 별도로 지정하고 [라인 4],

이 것을 사용할 SQL에서 가져오고자 할 때 사용한다 [라인 16, 22, 32].

이때 ID가 일치해야 한다.


if 문은 다음 코드처럼

@{if test="조건식"}실행할 내용{/if}으로 사용한다.

정규식을 이용한 파서에 문제가 있어서 IF문에만 @를 사용했는데,

일관성을 위해 include도 @{include}로 처리할지 고려 중이다.


if 문은 board5.xml 파일에 다음과 같이 작성되어 있다.

board5.xml

조건식은

숫자 변수를 사용할 경우에는 $변수 > 10과 같이 사용하고

문자 변수를 사용할 경우에는 $변수 === "test"와 같이 사용한다.

Mybatis와 마찬가지로 else 문이 없다.

따라서 예제와 같이 두개의 if문을 사용해야 한다.


데이터를 정해진 개수(cnt) 만큼 가져올 때와 [라인 6]

정해진 개수가 없거나($cnt === undefined), 0 이면 [라인 11]

모든 데이터를 가져오도록 조건을 사용하지 않았다.


명령어

설명

예제

 #{변수명}

 문자열 변수 (PrepareStatement)

 #{cnt}

 ${변수명}

 숫자형 변수 (Statement)

 ${cnt}

 {include}

 공통 SQL 문 가져오기

 {include ref='sql 태그 id'}

 @{if test="조건식"}

 비교연산자

 @{if test="cnt>1"}실행할 내용{/if}


이렇게 작성한 SQL문을

nineBatis(9batis)를 이용하여 프로그램(js)에서 호출해서 사용한다.


9batis는 다음과 같이 선언하여 사용한다.

var batis9 = require("./nineBatis");
batis9.loadQuery(__dirname +'/sql', true);

9batis 파일(nineBatis.js)은

아직 npm에 등록하지 않아서 nineBatis.js 파일을 복사해서 사용해야 한다.

샘플 프로젝트의 routes 폴더에 다른 코드들과 같이 있다.

(등록은 어느 정도 쓸만하다 싶을 때 할 예정)


require로 nineBatis.js 파일을 가져오기 한 후,

9batis의 loadQuery() 함수로 xml 파일들을 읽어들인다.

파라미터로 xml파일들이 있는 폴더(sql)를 지정하고,

디버그 여부를 지정하면 된다.

디버그를 true로 지정하면 console에 현재 실행하는 SQL문이 출력된다.


Node.js를 Eclipse에서 실행한 경우에는 하단 console 탭에 SQL ID와 SQL문이 출력된다.

Node.js를 cmd 창에서 실행한 경우에는 cmd 창에 SQL ID와 SQL문이 출력된다.

SQL ID와 SQL문이 출력이 중요한 것이

ID로 문제가 되는 SQL을 쉽게 찾을 수 있고

출력된 SQL 문을 복사해서

전용 Client (WorkBench, HeidiSQL등)에서 붙여넣고 실행하면

쉽게 SQL 오류를 찾을 수 있기 때문이다.


loadQuery()는 한 프로젝트에서 한번만 실행해야 하고

커넥션 풀링처럼

9batis을 app.js에서 실행한 후

변수 batis9 을 글로벌로 사용하는 것이 좋다.


먼저, 9batis를 소극적으로 사용한 예제이다.

board3.js

예제로, 모든 데이터를 DB에서 가져와 출력하는

게시판 글 리스트 부분을 정리하면,

기존에는 다음과 같이 SQL문이 프로그램 내에서 문자열로 사용되었다.

router.get('/list', function(req,res,next){
    pool.getConnection(function (err, connection) {
        var sql = "SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE" +
                   " FROM TBL_BOARD";
        connection.query(sql, function (err, rows) {
            if (err) console.error("err : " + err);

            res.render('board1/list', {rows: rows});
            connection.release();
        });
    });
});

board1.js

9batis에서는

이 SQL문을 XML (board3.xml)로 저장하고,

ID (selectBoardList)를 부여하여 작성한 뒤에

컨트롤에서는 이 ID로 호출하면 (batis9.getQuery())

해당 SQL문을 반환 받아서 사용하게 된다 [라인 15].

이것이 9batis의 기본 사용 방법이자 기본 개념이다.


조금 더 복잡한 기능으로

SQL문에 필요한 파라미터를 지정 방법에 대하여 정리한다.

기존에는 글읽기와 글저장 부분의 SQL문이

다음과 같이 문자열로 사용되었다.

router.get('/read', function(req,res,next){
    pool.getConnection(function (err, connection) {
        var sql = "SELECT BRDNO, BRDTITLE, BRDMEMO, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE"+
                   " FROM TBL_BOARD" +
                  " WHERE BRDNO=" + req.query.brdno;
        connection.query(sql, function (err, rows) {
            ~~ 생략 ~~
        });
    });
});

router.post('/save', function(req,res,next){
    var data = [req.body.brdtitle, req.body.brdmemo, req.body.brdwriter, req.body.brdno];

    pool.getConnection(function (err, connection) {
        var sql = "";
        if (req.body.brdno) {
            sql = "UPDATE TBL_BOARD" +
                       " SET BRDTITLE=?, BRDMEMO=?, BRDWRITER=?" +
                  " WHERE BRDNO=?";
        } else {
            sql = "INSERT INTO TBL_BOARD(BRDTITLE, BRDMEMO, BRDWRITER, BRDDATE) VALUES(?,?,?, NOW())";
        }
        connection.query(sql, data, function (err, rows) {
            ~~ 생략 ~~
        });
    });
});

board1.js

SQL문에 필요한 파라미터는

글 읽기(/read)를 예로 할 경우 글 번호(brdno)를 의미한다.

지정된 글 번호에 따라 DB에서 글 내용을 조회하여 화면에 출력한다.

이 글 번호는 글 리스트에서 사용자의 선택에 따라 바뀌는 것으로

위 코드처럼 지정된 글 번호(req.query.brdno)에

맞는 데이터(WHERE)를 가지고 오도록

문자열 조작을 통하여 SQL문을 작성 한다.

" WHERE BRDNO=" + req.query.brdno


글 저장(/save)은 사용자가 입력한 값을 저장하기 위한 것으로

파라미터를 모두 ? 로 지정하고

배열 (data 변수)로 값을 넘기는 방식으로 작성한다.


9batis에서는

getQuery()를 호출할 때, JSON으로 파라미터를 지정한다 [라인 27, 41, 43].

SQL문 작성시 ${brdno}로 지정한 경우,

JSON의 키 이름을 brdno 로 하여 사용한다 [라인 27].

9batis에서 해당 변수에 JSON 값을 치환하여 SQL문을 반환한다.

(콘솔에 출력된 SQL문으로 확인할 수 있다.)

$는 SQL 문 변환시 숫자라는 의미로 값만 출력되고,

#은 문자라는 의미로 홑따옴표(')로 싸여서 출력된다.

    <sql id="includeWhere">
        WHERE BRDNO=${brdno}
    </sql>
 
    <update id="updateBoard">
        UPDATE TBL_BOARD
           SET BRDTITLE=#{brdtitle}, BRDMEMO=#{brdmemo}, BRDWRITER=#{brdwriter}
          {include refid="includeWhere"}
    </update>
    
    <insert id="insertBoard">
        INSERT INTO TBL_BOARD(BRDTITLE, BRDMEMO, BRDWRITER, BRDDATE)
        VALUES (#{brdtitle}, #{brdmemo}, #{brdwriter}, NOW())
    </insert>

board3.xml

이렇게 구현하면서 부수적인 효과는

기존에는 Node.js에서 제공하는 JSON 데이터(req.body)를

배열 (data 변수)로 변환해서 사용했지만

  var data = [req.body.brdtitle, req.body.brdmemo, req.body.brdwriter, req.body.brdno];

9batis에서는 JSON 데이터(req.body)를 그대로 사용한다 [라인 41, 43].

  sql = batis9.getQuery('insertBoard', req.body);


이번에는 9batis를 제대로 사용하는 방법(board4.js)을 정리한다.

board4.js

일단, 일부만 올린 board3.js 파일의 내용 보다 확 줄어든 코드 양을 볼 수 있다.

이것은 DB에 접속해서 SQL을 실행하고

실행 결과를 반환 받는 부분을 execQuery() 함수로 처리했기 때문이다.


다음과 같이 글 리스트(/list)에 대하여

기존에 작성한 코드와

9batis로 작성한 코드를 직접 비교하면 쉽게 알 수 있다.

router.get('/list', function(req,res,next){
    pool.getConnection(function (err, connection) {
        var sql = "SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE" +
                   " FROM TBL_BOARD";
        connection.query(sql, function (err, rows) {
            if (err) console.error("err : " + err);

            res.render('board1/list', {rows: rows});
            connection.release();
        });
    });
});

board1.js

execQuery() 함수를 호출하고

반환 받은 실행 결과인 rows을 render()에 파라미터로 지정하면 끝이다.

파라미터가 null인 것은 SQL문 생성을 위해 필요한 값이 없다는 것으로,

SQL문 생성을 위해 필요한 값이 있는 경우

앞서 정리한 예제와 동일하게 JSON 으로 지정하면 된다 [라인 19, 29, 37 43 참조].

router.get('/list', function(req,res,next){
    batis9.execQuery(pool, 'selectBoardList', null, function (rows) {
         res.render('board4/list', {rows: rows});
    });
});

board4.js

execQuery()의 파라미터는

DB접속 클래스, 실행할 SQL 아이디, SQL 실행에 필요한 파라미터(JSON), SQL 실행 완료 후 실행할 콜백 함수

의 4 가지 이다.

이 중 DB접속 클래스(pool)는

loadQuery() 호출시 미리 지정해서 사용하는 것이 편리하지만

이해를 위해 현재는 이렇게 구현했다.

메소드

설명

예제

 loadQuery

 - SQL문이 있는 XML 파일을 읽어서 보관.

 - 파라미터

   filepath: XML 파일이 있는 폴더
   mode: 디버깅 여부.

 batis9.loadQuery(__dirname +'/sql', true)

 getQuery

 - 지정된 ID의 SQL문을 찾아서 반환.

 - 파라미터

   id: XML 파일에서 지정된 SQL문의 id
   params: SQL문에 지정된 변수의 값 (Json)

 batis9.getQuery('selectBoardList');

 batis9.getQuery('selectBoardOne', {brdno: 1});

 execQuery

 - 지정된 ID의 SQL문을 찾아서 실행

 - 파라미터

   pool: DBMS에 대한 연결
   id: XML 파일에서 지정된 SQL문의 id
   params: SQL문에 지정된 변수의 값 (Json)
   callback: SQL문 실행후 호출할 함수

batis9.execQuery(pool, 'selectBoardList', null, function (rows) {
    res.render('board4/list', {rows: rows});
});


마지막으로 2개 이상의 SQL문을 실행하는 것과

하나의 SQL문으로 두 가지 기능(IF문)을 하는

다소 복잡해 보이는 예제(board5.js)를 정리한다.

board5.js

먼저, 9 라인 과 10 라인을 보면

두 개의 SQL문이(selectBoardOne, selectBoardList) 사용된 것을 볼 수 있다.


글 읽기(/read)에서

지정된 글 내용을 DB에서 가지고 온다(selectBoardOne).

이 기능에 이전 글에 대한 정보(selectBoardList5)를 제공하는 기능을 추가하였다.

현재 글을 모두 읽은 후

글 리스트로 돌아가서 다른 글을 선택하는 것이 아니라

글을 읽던 페이지에서 바로 이전 글을 읽을 수 있도록 하는 것이다.

(다음 글 기능은 각자 구현해 보길)


두 개의 SQL문을 사용하는 것은

기존의 Node.js에서 사용하는 것이랑 동일하게

하나의 SQL문이 실행 완료되면 [라인 9],

지정된 콜백 함수에서 나머지 SQL문을 실행하도록 하면 된다 [라인 10].

이 기능은 구현한 것이 아니고 Node.js의 기능을 그대로 쓰는 것이다.


다음으로 하나의 SQL문(board5.xml)으로 두 가지 기능을 하도록 구현한다.


글 읽기에서 이전 글에 대한 정보를 가지고 오는 것은

글 리스트와 거의 유사한 SQL문을 사용한다.

이전 글은 현재 글 보다 글 번호가 적은 글 중 하나를 가지고 오는 것이다.

글 리스트(/list)는 그냥 모든 글을 가지고 온다.


이 둘의 공통 점은 글에 대한 정보(글 제목, 작성일자 등)를 가지고 오는 것이고

차이점은

조건에 의한 하나의 행(LIMIT)을 조회하느냐,

또는 여러 개의 리스트를 가지고 오느냐 이다.


board5.xml

기존에 사용하던 selectBoardList를 두고

예제를 위해 별도로 selectBoardList5로 새로 생성하였고,

출력할 데이터 개수(cnt)를

지정하면(if) 이전 글을 찾는 SQL문을 [라인 6~10],

지정하지(else) 않으면

조건을 사용하지 않아 모든 데이터가 조회된다 [라인 11~13].


이 SQL문을

글 리스트(/list)에서는 별 지정 없이(null) 호출하고 [board5.js 라인 3]

batis9.execQuery(pool, 'selectBoardList5', null,


글 읽기(/read)에서는 {cnt: 1}과 같이 파라미터를 지정한다 [board5.js 라인 10].
    batis9.execQuery(pool, 'selectBoardList5', {brdno: req.query.brdno, cnt: 1},



이상으로 nineBatis (9batis)의 사용법을 정리하였다.

9batis는 SQL문을 XML 파일에 저장하고,

프로그램에서 간단하게 호출하여 (execQuery)

사용할 수 있도록 도와 주는 Node.js 라이브러리로

여기에서 다운로드 받을 수 있다.




개발자가 되기 위해 공부하던 한 대학생이

웹 개발의 기초라는 게시판을 제작하는 것은 하겠는데

간단한 웹 사이트 하나를 못 만들겠다고 말했다.

출처: pxhere.com

그래서

개발은 잘하는데(?), 일은 못하는 상태 같다고 말했다.


반대로, 현재 일하고 있는 회사 직원들은

개발 능력은 뛰어나지 않지만

내외부 프로젝트를 별문제 없이 완료하니

개발은 잘 못하는데 일은 참 잘하는 개발자라고 말했다.

SQL도 제대로 못 만드는 직원이

제법 복잡한 웹 사이트를 혼자서 만들기도 했다.

(기존 소스를 참고해서)

출처: blog.aladin.co.kr

이 둘의 차이는

하나의 프로젝트를 해본 사람과 안 해본 사람의 차이 같다.

잘 하든 못 하든,

하나의 프로젝트를 혼자서 해본 사람은

개발하는 과정이 (일하는 방법이) 머릿속에 그려지고

어떻게든 풀어나가지만

처음 하는 사람은 배가 산으로 가는 경험을 하게 될 수도 있다.


비슷한 예(?)로,

토익 점수는 높은데, 영어로 대화하지 못하는 것과 비슷한 것 같다.

외국인과 대화를 하지 못하는 이유 중 하나는 대화를 많이 안했기 때문일 것이다.

외국인과 자주하다 보면 자연스럽게 회화가 될 것이다.

SW 개발도 이와 같다고 생각한다.

출처: pixabay.com

기초가 매우 중요하지만

꿩 잡는 게 매라고

어떻게든 개발해내고,

그 개발에서 나오는 자신에 대한 믿음이 중요하다고 생각한다.

출처: pxhere.com

'개발 이야기' 카테고리의 다른 글

4살과 국회의원  (0) 2018.04.12
두 마리 소  (0) 2017.10.13
개떡같이 이야기해도 찰떡같이 알아듣는다.  (1) 2017.06.08
누울 자리 봐 가며 발 뻗어라.  (0) 2017.05.04
꼴뚜기가 뛰니 망둥이도 뛴다  (0) 2017.05.04

Eclipse에서 Node.js (Nodeclipse)를 사용하는 방법을

다음과 같이 4가지로 정리하였다.

1. Node.js 프로젝트 생성 및 개발 준비

2. MariaDB 기반 CRUD 게시판 만들기

3. 게시판 예제 보강

4. GitHub에서 Node.js 소스 가져오기


Node.jsNodeclipse 설치는

인터넷을 찾아 보길 바라고, 여기서는 설치한 이후부터 정리한다.

Express 기반으로 제작하기 때문에

Express (express-generator 포함)도 설치해야 한다.


Node.js는 Atom이나 notepad++로 작성하고

콘솔에서 실행해서 개발해도 되지만,

디버깅과 사용의 편의성 때문에 Eclipse에서 작성하는 것을 선호한다.

(타이핑을 안 좋아해서 GitHub도 eclipse로 이용)

Nodeclipse은 eclipse에서 Node.js를 사용할 수 있게 해주는 플러그인으로

Eclipse의 marketplace에서 Nodeclipse로 검색해서 설치하면 된다.


개발을 위해 새로운 프로젝트(board)를 생성한다.

새 프로젝트는 Nodeclipse를 이용하여 생성할 수 있지만 (그림참조),

웹 개발에 필요한 기본 설정을 제대로 작성해주지 않기 때문에

express로 콘솔 창에서 프로젝트를 생성하고,

Eclipse에서 Import 해서 사용하는 방법을 이용한다.



먼저 console 창에서

다음과 같이 입력하여 사용할 프로젝트를 간단하게 생성한다.

board 만 원하는 이름으로 지정하면 된다.

> express --session --view=ejs --css css board

> cd board

> npm install



express-generator에서 웹 개발에 필요한 여러가지 설정을 해주기 때문에

위와 같이 콘솔 창에서 프로젝트를 생성하는 것이 더 좋다.


주의: 콘솔 창에서 express 명령어를 사용하려면 express-generator를 설치해야 한다.

> npm install express-generator -g


콘솔창에서 [npm start]를 입력하여 웹 서버를 실행한다.

웹 브라우저에서 [http://localhost:3000/]를 입력하여

다음 그림과 같이 실행되면,

제대로 Node.js용 웹 프로젝트가 생성된 것이다.


콘솔 창에서 실행한 Node.js를 중지하고,

Eclipse를 실행해서

콘솔창에서 생성한 프로젝트를 가져오기(import)한다.


Eclipse에서 File > Import 를 실행하고

"Projects from Folder or Archive"을 선택한다.


"Directory" 버튼을 클릭해서,

express(express-generator)로 생성한 프로젝트의 폴더를 지정하고,

"Finishi" 버튼을 클릭한다.


[Project Explorer] 뷰에서

가져오기 한 board 프로젝트의 파일들을 볼 수 있다.

node_modules 폴더에 빨간색 X 표시가 된 것은

몇몇 라이브러리가 제대로 설치되지 않은 경우 나타나는 것으로

예제 구현에 문제가 없으니 넘어간다.


bin 폴더 하위에 있는 www 파일을 선택하고

컨택스트 메뉴에서 Run As > Node Application을 실행한다.

앞서 콘솔 창에서 [npm start]를 실행한 것과 같이

eclipse에서 웹 사이트를 실행하는 방법이다.


다음 그림 하단의 콘솔(console) 탭에 보이는 것처럼

종료(Teminate) 버튼이 빨간색으로 나타나면

웹 사이트가 제대로 실행된 것이다.

웹 브라우저에서 [http://localhost:3000/]를 입력하여 접속을 확인한다.


bin 폴더 하위에 있는 www 파일을 선택하고

컨택스트 메뉴에서 Run As > Node Application을 실행하는 대신에,

Debug As > Node Application을 실행하면,

Node.js를 손 쉽게 디버깅하면서 개발 할 수 있다.

console.log로 변수의 값을 출력하면서 디버깅 할 수도 있지만

Java로 개발하듯이,

Eclipse에서 Breakpoint를 설정하고 한 행씩 실행하면서 디버깅 할 수 있다.


이상의 그림은 디버그 모드로 실행했을 때 제공되는 Debug Persperctice 화면으로,

현재 행까지 실행하고,

각 변수의 값을 우측 상단에 있는 Variables 탭에서 확인할 수 있다.

그림에서는

rows 배열 변수의 값과 각 원소의 Json 값이 출력되는 것을 볼 수 있다.


'Node.js > 기타' 카테고리의 다른 글

우리동네, 아이랑 놀곳 Ver.서울  (0) 2019.05.29
nineBatis (9batis)  (2) 2017.08.16
2. MariaDB 기반 CRUD 게시판 만들기  (23) 2017.08.05
3. 게시판 예제 보강  (4) 2017.08.05
4. GitHub에서 Node.js 소스 가져오기  (0) 2017.08.05

Eclipse에서 Node.js (Nodeclipse)를 사용하는 방법을

다음과 같이 4가지로 정리하였다.

1. Node.js 프로젝트 생성 및 개발 준비

2. MariaDB 기반 CRUD 게시판 만들기

3. 게시판 예제 보강

4. GitHub에서 Node.js 소스 가져오기


앞서 생성한 board 프로젝트에 간단한 게시판 기능을 구현하여

기초적인 Node.js 사용법을 정리한다.

MariaDB(MySQL)을 기반으로

데이터를 저장하고 조회하는 CRUD 게시판을 제작한다.


게시판과 관련된 기본 설명들은 본 블로그에 정리되어 있다.

Java(Spring) 기반으로 되어 차이가 있지만

대부분의 개념은 언어와 관계없이 유사하니 참고하면 도움이 될 것이다.


여기서는 Java로 작성된 게시판 예제 중 게시판 1을 Node.js로 구현한다.

HTML부분은 그대로 복사해서 사용하고, 컨트롤 부분만 간단하게 작성하였다.


먼저, MariaDB 에서

다음과 같이 테이블(TBL_BOARD)을 생성한다.

CREATE TABLE TBL_BOARD (

    BRDNO int(11) NOT NULL AUTO_INCREMENT,         -- 글 번호

    BRDTITLE varchar(255),                                -- 제목

    BRDWRITER varchar(20),                              -- 작성자

    BRDMEMO   varchar(4000),                           -- 내용

    BRDDATE     datetime,                                -- 작성일자

    PRIMARY KEY (BRDNO)
) ;


Node.js에서 MariaDB에 접속하기 위해 mysql(?) 라이브러리를 설치한다.

콘솔 창에 [npm install mysql --save]를 입력하여 설치한다.

Eclipse에서 package.json 파일을 열어보면

mysql이 추가된 것을 볼 수 있다.

mysql이 이미 설치된 경우 콘솔 창에서 설치 할 필요없이

package.json 파일에서 다음과 같이 입력하고

컨택스트 메뉴에서 Run As > npm Install를 실행해도 된다.


먼저, app.js 파일을 열어서 다음 문장을 추가한다.

app.use('/board1', require('./routes/board1'));

routes 폴더에 있는 board1.js 파일을

board1 이라는 가상 폴더(/)로 사용하겠다는 것이다.

웹에서 board1/list, board1/form 과 같이 입력해서 사용한다.

웹 컨트롤 파일(js)은 대부분 routes 폴더에 두고 사용하고

HTML(ejs)과 같은 뷰(view) 파일은 views 폴더에 두고 사용한다.


board1.js의 내용은 다음과 같다.


모든 코드를 정리 하지 않고, 주요한 몇 가지만 정리한다.


먼저, MariaDB (MySQL)에 접속하는 커넥션 풀링을 생성한다 [라인 5~12].

이 커넥션 풀링(pool)을 이용하여 SQL문을 실행한다.

적당한 DB 접속 정보를 지정하면 된다.


14 라인의 / 은 구체적인 URL을 입력하지 않고

가상 경로(board1)만 입력하면

게시판 리스트(/board1/list)로 이동시킨다.


데이터 CRUD(Create, Read, Update, Delete)를 구현하기 위해

list, read, form, save, delete의 5 개의 컨트롤(URL)이 정의되었다.

이 컨트롤은 웹에서

    http://localhost:3000/board1/list

로 입력해서 사용한다.


list, read, form, save, delete의 컨트롤은

각각 글 리스트, 글 읽기, 글쓰기 폼, 글쓰기 저장, 글 삭제를 의미한다.

글 쓰기과 글 수정은 같은 개념이라 하나로 구현했다.

자세한 설명은 여기를 참고 하면 된다.


각 컨트롤의 코드가 제법 많아 어려워 보일 수 있는데

자세히 보면 거의 비슷한 코드로 작성된 것을 알 수 있다.

따라서, 여기서는 예제로 글 리스트(list)만 정리한다.


글 리스트(/list)가 호출되면 [라인 18]

DB에서 게시판 테이블(TBL_BOARD)의 데이터를 가지고 (SELECT) 오도록 한다 [라인 57].

SQL문을 실행하는 명령어는 connection.query로

SQL에 필요한 파라메터가 필요할 경우에는 [라인 79]

다음과 같이(data) 배열로 지정하고

    var data = [req.body.brdtitle, req.body.brdmemo, req.body.brdwriter, req.body.brdno]; [라인 67]

    connection.query(sql, data, function (err, rows)

없으면 생략한다 [라인 22, 38, 57].

글 읽기(삭제)에서는 파라메터를 SQL 문자 조작을 통해서 지정했다 [라인 91].


다시 글 리스트(/list)에서

SELECT 문을 실행한 결과가 rows 변수로 반환된다 [라인 22].

이 것을 HTML(ejs) 파일에 보내서 (res.render) 적당한 디자인으로 출력한다 [라인 26].


res.render는

지정된 HTML(ejs) 파일을 찾아서

지정된 데이터(rows)을 연결해서 처리하라는 함수이다[라인 26].

res.render에서 지정된 board1/list는

views폴더 하위에 board1 폴더 하위에 list.ejs 파일을 의미한다.

views폴더는 기본 설정이라 이 문장에서 생략된다.

ejs 확장자도 생략하여 사용한다.


글 리스트, 글쓰기 폼, 글 읽기는

데이터 처리후 시각적인 페이지가 있기 때문에

각각 list.ejs, form.ejs, read.ejs 파일이 있어야 하고

글 저장, 삭제는 시각적인 페이지가 없기 때문에

컨트롤 처리 후 이동할 URL(redirect)을 지정한다 [라인 82, 95].


정리하면,

컨트롤(js)에서는 데이터 베이스에 접속해서 데이터를 가져오거나 전송하고

처리 결과에 따라

필요한 HTML (ejs) 파일을 호출한다.


다음은 board1 폴더 하위에 있는 list.ejs 파일의 내용이다.

전체 코드는 기존에 사용하던 HTML 이고

27 라인 부터 38 라인이 ejs에 의한 자바스크립트 문법이다.

ejs 파일에서 <% %> 사이에 자바 스크립트를 사용한다.


컨트롤에서 지정된 데이터(rows)의 개수만큼 반복해서 [라인28]

HTML 테이블 태그의 TR을 생성한다.

하나의 TR이 데이터 한 행을 출력한다.

하나의 TD가 데이터 한 행의 한 열을 출력한다.


SQL SELECT 문을 실행한 결과가

rows란 배열로 반환되어

rows[i]로 한 행씩 찾아서

JSON으로 구성된 각 행의 열에 있는 값을 출력(=)한다 [라인 31~34].

각 열의 이름과 이름이 대문자로 사용된 것은

SELECT 문 사용시 사용한 이름이고

이때 대문자로 사용했기 때문이다.

SELECT BRDNO, BRDTITLE, BRDWRITER, DATE_FORMAT(BRDDATE,'%Y-%m-%d') BRDDATE


나머지 글쓰기 폼(form.ejs), 글 읽기(read.ejs)도

각각 다음과 같이 작성하여 board1 폴더에 저장한다.

form.ejs

read.ejs



웹 브라우저에서 http://localhost:3000/board1/list를 입력하여

그림과 같이 잘 실행되는지 확인한다.

좌측 상단의 [글쓰기] 버튼을 클릭한다.

내용을 작성하고 [저장] 버튼을 클릭한다.

글이 잘 등록되었으면

글 제목을 선택해서 글 읽기로 이동한다.


Java(Spring) 기반의 게시판 예제 중 하나를

Node.js로 제작하였다.

Java(Spring) 기반의 게시판 예제를 모두 Node.js로 변환한다면

실력향상에 많은 도움이 될 것이니 도전해 보길 바라고,

도전 결과를 공유하였으면 좋겠다.


지금까지 정리한 소스는 github에서 받을 수 있고,

설치 방법은 여기에 정리하였다.


팁: Eclipse에서 ejs 파일 열기

Eclipse에서 ejs 파일을 열면

다음 그림과 같이 일반 텍스트(Text) 파일 편집기로 열린다.

이 경우 가독성이 떨어지기 때문에

예약어 (HTML 태그)별로 색상이 있는 것이 좋다.


[Prjoect Explorer]뷰에서 ejs 파일 하나를 선택하고 컨텍스트 메뉴를 실행한다.

Open With > Other 메뉴를 실행하고

편집기로 HTML Editor를 선택한다.

하단의 체크 박스도 선택하고 [OK] 버튼을 클릭한다.


아무 ejs 파일을 열면

그림과 같이 보기 좋게 정리된 것을 볼 수 있다.






'Node.js > 기타' 카테고리의 다른 글

우리동네, 아이랑 놀곳 Ver.서울  (0) 2019.05.29
nineBatis (9batis)  (2) 2017.08.16
1. Node.js 프로젝트 생성 및 개발 준비  (0) 2017.08.05
3. 게시판 예제 보강  (4) 2017.08.05
4. GitHub에서 Node.js 소스 가져오기  (0) 2017.08.05

+ Recent posts