끊임없이 검증하라

나에게 당연할지라도

Project

P2_페이지 내 하이퍼 링크 달아주는 코드_1_단순로직

fadet 2022. 6. 8. 00:08

* 이 포스트는 학습 과정에서 그 내용을 기록한 글이기에 부정확한 정보가 포함될 수 있습니다.

따라서 해당 글은 참고용으로만 봐주시고 틀린 부분이 있다면 알려주시면 감사하겠습니다. 

 


 

목록
1 개발 환경
2 준비
3 input.txt 불러오기
4 원본 html코드
5 기능 추가 전 준비
6 기능 추가
7 코드 마무리
개발 환경

 

우선 이후 페이지로 로직을 이식할 예정이라 JAVA, Spring boot를 사용할 예정입니다. 추후 글을 볼 필요가 없으시면 spring 필요 없이 그냥 jdk 1.8 이상만 사용하시면 됩니다.

  • JDK 11, IntelliJ
  • Spring boot
준비

 

이전 포스트의 내용대로 1 index 자동 완성 2 페이지 내 링크 자동 완성 기능에 대한 로직을 우선 완성하겠습니다. 일단 로직은 포스트의 html 코드를 복사한 input.txt 파일을 읽고 로직을 실행하여 result.txt 파일을 출력하고 해당 문서를 복사하여 다시 티스토리에 붙여넣는 과정을 거치도록 가장 큰 틀을 잡겠습니다.

 

 

input.txt 불러오기

 

public class Logic {
    public static void main(String[] args) throws IOException {

        /*
        준비
         */

        // 텍스트 불러오기
        FileReader reader = new FileReader("D:\\input.txt");

        String inputText = "";
        int charTxt;
        while ((charTxt = reader.read()) != -1) {
            inputText += (char)charTxt;
        }

charTxt는 원본 html코드의 한글자씩 string인 inputText으로 변환하기 위해 도입했습니다. reader.read()는 input.txt를 reader가 한 글자씩 읽고 문자를 출력하는데 값이 없으면 -1을 반환합니다. 따라서 charTxt를 int로 미리 선언한 뒤 while문으로 -1이 될 때까지 inputText에 추가하여 원본 html코드를 inputText라는 string객체로 변환합니다.`

 

원본 html코드

 

티스토리 블로그 원문은 아래와 같이 생겼고 중요 부분을 표시했습니다.

위 원문의 html코드는 아래와 같고 수정해야할 부분은 동일한 색으로 표시했습니다.

위의 html코드는 result.txt로 완성된 이후 index가 모두 채워졌을 때이며 input.txt에 복사될 코드는 아래입니다.

두 html코드를 비교해보면 앞으로 해야할 일을 대략적으로 파악할 수 있습니다. 1 index는 비워진채로 입력될 것이기에 자동으로 입력되어야 하며 2 파란 부분의 id 부분이 자동으로 입력되어야 합니다. 따라서 수정이 필요한 부분은 index 하위의 내용만 필요하기에 서론 부분을 제외한 나머지 부분만 수정할 예정입니다.

 

따라서 최종적으로 최초 로직은 다음과 같은 순서로 짜면 될 것 같습니다.

1. 원본 html 코드를 파일로 입력받아 string으로 변환
2. index부터의 내용을 따로 추출
3. 각 목차(title)의 총 개수와 그 내용을 따로 추출
4. 타이틀 별 blockquote태그에 id를 다는 기능 추가
5. index 자동 완성 기능 추가
6. 수정된 html 코드를 파일로 출력

 

이 글을 보고 따라 하실 분들은 3, 4, 5만 유의 깊게 보시면 쉽게 따라하실 수 있을겁니다. 따라하시기 쉽게 로직에 stream 관련 코드를 일절 사용하지 않았기 때문에 코드만 봐도 이해하실 겁니다.

 

기능 추가 전 준비

 

위에 작성된 순서의 2번부터 시작하겠습니다.

 

#2 index부터의 내용을 따로 추출

// 인덱스 개수 + 타이틀 저장
int startIndex = inputText.indexOf("Index");
String startText = inputText.substring(startIndex);

일단 위 코드로 원본 inputText에서 index 아래 내용만 따로 추출합니다. 서론은 중간 로직이 태그들을 분리할 때 의도치않은 중복 등 오류가 발생할 수 있기때문에 startText를 통해 타이틀을 구분할 예정입니다.

 

#3 각 목차(title)의 총 개수와 그 내용을 따로 추출

 

다음으로 순서 3번을 진행하기 전에 수정할 html코드를 살펴보겠습니다. 

<blockquote data-ke-size="size16" data-ke-style="style1"><span style="font-family: 'Noto Serif KR';">가치, 그리고 화폐</span></blockquote>

타이틀 별로 인용문으로 시작하기에  태그의 시작은 <blockquote로 시작하며 title명은 span태그를 닫는 순간부터 시작됩니다.

 

따라서 각 목차를 인식하는 변수인 countTitleKeyword를 선언하고 그 값을 다음과 같이 할당해줍니다. 또한 title의 개수를 셀 count 변수도 같이 선언합니다.

String countTitleKeyword = "blockquote data-ke-size=\"size16\" data-ke-style=\"style1\">";
int count = 0;

 

이제 타이틀을 따로 추출하기위해 startText를 전부 태그의 시작 꺽쇠< 로 나누어 배열로 변환합니다. 변환 후 배열의 길이인 textLen도 선언해줍니다.

String[] startTextArrByBracket = startText.split("<");
int textLen = startTextArrByBracket.length;

 

이제 수정 전 타이틀의 리스트인 oldTitleList를 선언해주고 for문을 통해 html코드를 잘게 자른 배열들 중 blockquote ...로 시작하는 요소들을 전부 oldTitleList에 집어넣습니다. 

List<String> oldTitleList = new ArrayList<>();
for (int i = 0; i < textLen; i++) {
    if (startTextArrByBracket[i].equals(countTitleKeyword)) {
        count++;
        // 수정이 덜 된 타이틀명 스트림으로 저장
        oldTitleList.add(startTextArrByBracket[i+1]);
    }
}

여기서 이해를 위해 조금 더 설명하자면 원본 html코드는 아래와 같습니다.

// 원문
<blockquote data-ke-size="size16" data-ke-style="style1"><span style="font-family: 'Noto Serif KR';">가치, 그리고 화폐</span></blockquote>

타이틀별로 태그가 인용구 태그인 <blockquote>, 내용 태그인 <span> 두 개로 이루어져 있으며 span태그에 글씨체에 관련된 style이 있습니다.

 

이를 분리한 배열 startTextArrByBracket에 저장되는 요소들은 <를 기준으로 구분되므로 

// startTextArrByBracket
요소 1 : blockquote data-ke-size="size16" data-ke-style="style1">
요소 2 : span style="font-family: 'Noto Serif KR';">타이틀1
요소 3 : /span></blockquote>

이렇게 나뉠 것이므로 우리에게 필요한 oldTitleList는 위의 요소 2가 필요합니다. 따라서 keyword로 요소 1을 찾고 요소 2를 oldTitleList에 추가하는 startTextArrByBracket[i+1] 코드를 씁니다.

 

 

기능 추가

 

#4 타이틀 별 blockquote태그에 id를 다는 기능 추가

이제 본격적으로 기능을 추가하겠습니다. 먼저 타이틀의 태그마다 a href로 이동하기 위해 id를 매겨줍니다. id를 매기기 위해 당연히 타이틀마다 태그를 쪼개야하겠죠? 

 

타이틀별로 공통 포함 keyword를 추가해줍니다.

String splitTitleKeyword = "blockquote data-ke-size=\"size16\" data-ke-style=\"style1\">";

 

다음으로 inputText를 keyword를 기준으로 쪼갭니다. 여기서 서론을 제거한 startText를 안 쓰고 inputText를 쓰는 이유는 앞서 내용 분리를 위해 startText를 사용한 것과 달리 우리는 이제 서론을 모두 포함한 html 코드 전체를 수정해야하기 때문입니다.

 

inputText를 keyword를 기준으로 분리하여 배열로 만들고 수정 이후 html코드가 될 addTitleText를 서론에 해당하는 배열의 0번째 요소를 갖도록 선언 후 초기화해줍니다.

String[] inputTextArrByKeyword =  inputText.split(splitTitleKeyword);
String addTitleText = inputTextArrByKeyword[0];

 

여기서 중요한 점은 splitTilteKeword로 <blockquote... 부분의 태그를 사용했기 때문에 해당 문장은 기준 keyword로 사용될 뿐만 아니라 배열에 해당 내용이 지워진채로 inputTextArrByKeyword가 생성되며 요소들은 다음과 같습니다.

//inputTextArrByKeyword
요소 0 : 맨 처음 ~ 서론
요소 1 : [<blockquote ... 문장 삭제] <span style="font-family: 'Noto Serif KR';">타이틀1 ~ 타이틀 2 이전 내용
요소 2:  [<blockquote ... 문장 삭제] <span style="font-family: 'Noto Serif KR';">타이틀2 ~ 
...

 

따라서 addTitleText를 미리 서론을 넣어 생성해 준 것 입니다. 이 다음에는 이전과 같이 배열의 개수인 arrLen을 이용해 for문을 돌립니다.

int arrLen = inputTextArrByKeyword.length;
for (int i = 1; i < arrLen; i++) {
    addTitleText += "blockquote data-ke-size=\"size16\" data-ke-style=\"style1\" "+"id=\""+i+"\">";
    addTitleText += inputTextArrByKeyword[i];
}

앞서 keyword로 <blockquote ... 를 사용했기에 해당 문장이 전부 삭제되었으므로 이제 id를 포함하도록 문장을 고쳐서 addTitleList에 추가합니다. id는 1부터 시작하는 정수입니다.

 

#5 index 자동 완성 기능 추가

 

타이틀 쪽은 마무리되었으므로 나머지 index부분만 마저 완성해주면 됩니다. 여기선 addTitleText를 이어서 사용합니다. 일단 원본 inputText는

서론~
<p data-ke-size="size16">&nbsp;</p>
<blockquote data-ke-style="style2">Index</blockquote>
<p data-ke-size="size16">&nbsp;</p>
~ 본문
...

이렇게 index만 인용문 안에 들어가므로 앞서 기능1에서 사용했던대로 index를 기준으로 배열을 분리하여 해당 문장을 없애고 다시 index의 내용을 완성해서 다시 붙여넣어 줄겁니다.

 

※ 주 의
저는 이후 포스트를 위해 그냥 Index를 addTitleText.split의 구분자로 사용했습니다만
이렇게 그냥 진행하시면 본문에 Index를 쓰는 순간 코드가 작동하지 않습니다.
다음 글을 읽지 않으실거라면 <blockquote data-ke-style="style2">Index</blockquote>를 전부 구분자로 지정해주셔야 코드가 작동합니다.

 

일단 addTitleText를 index를 기준으로 두 개로 쪼개 배열로 생성합니다. 이후 index 이전 부분을 다시 Index를 붙여 resultText에 집어넣습니다. 

String[] resultArr = addTitleText.split("Index");
String resultText = resultArr[0]+"Index";

 

여기서 addTitleText 와 resultText를 헷갈리실 수도 있어 정리하자면 아래와 같습니다.

inputText : 수정 전 원본 html 코드
addTitleText : 기능 1을 거쳐 수정된 1차 수정 코드
resultText : 수정된 addTitleText를 다시 기능 2를 거치도록 한 수정 코드. 최종 result.txt에 들어감

 

이제 마저 완성을 합니다. 앞서 기능 추가 전 준비에서 추출했던 oldTitleList를 쓸 예정입니다. 하지만 oldTitleList는 각 요소가 [ span style="font-family: 'Noto Serif KR';">타이틀1 ] 처럼 되어 있으므로 해당 문장에서 타이틀1만 따로 뽑으려면 >를 기준으로 앞 뒤로 쪼갠 뒤 뒷 부분을 추출할겁니다.

List<String> titleList = new ArrayList<>();
for (int i = 0; i < oldTitleList.size(); i++) {
    titleList.add(oldTitleList.get(i).split(">")[1]);;
}

 

위 코드 이후 titleList의 요소는 아마 타이틀1, 타이틀2 ... 처럼 타이틀명만 가집니다. 이제 이 타이틀명을 index에 포함하여 index를 마저 작성해줍니다.

for (int i = 0; i < titleList.size(); i++) {
    int j = i+1;
    resultText += "<br /><a href=\"#"+j+"\">"+j+" "+titleList.get(i)+"</a>";
}
resultText += resultArr[1];

 

 

코드 마무리

 

#6 수정된 html 코드를 파일로 출력

 

이제 try catch문을 사용하여 수정 결과 코드인 result.txt를 생성하도록 해줍니다.

    /*
    결과 출력
     */

    // reult.txt 생성
    try {
        File file = new File("D:\\result.txt");

        try {
            if (file.createNewFile()) {
                System.out.println("File created");
            } else {
                System.out.println("File already exists");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        FileWriter fw = new FileWriter(file);
        PrintWriter pw = new PrintWriter(fw);

        pw.write(resultText);
        pw.close();

    } catch (IOException e) {
        e.printStackTrace();
    }
}

파일 생성 과정에서 예외가 발생할 수 있기에 try catch를 사용해줬는데 실습하실땐 안 써도 됩니다.

 


전체 코드는 아래 repo에 있습니다. 티스토리가 아닌 다른 블로그는 안 써봐서 잘 모르는데 blockquote만 다를 것 같아 크게 위 로직과 달라질 건 없어보입니다. 해당 포스트의 index는 코드가 작동하는지 볼 겸 소개한 코드를 바탕으로 완성했습니다.

 

이번 포스트는 실습을 위한 로직 설명에 집중했고 검증이나 다른 부분은 이후 포스트를 진행하며 소개하겠습니다. 사실 해당 내용은 참고 없이 전부 스스로 하다보니 실수가 좀 있네요. 당장 주의에도 적은 것처럼 이번 포스트를 쓸 때 코드에서 Index는 구분자기 때문에 본문에 index를 중복해서 사용하면 안되는걸 잊어버리는 등 검증과 관련된 부분은 중요하기에 일부러 다음 포스트에서 수정하도록 하겠습니다.

 

 

 

 

refer

https://github.com/kth1017/practice_linkInPage

 

GitHub - kth1017/practice_linkInPage: 블로그 포스트용 링크 생성 코드

블로그 포스트용 링크 생성 코드. Contribute to kth1017/practice_linkInPage development by creating an account on GitHub.

github.com