๐ŸŒธ [SPRING BOOT] | [JS] | ๐ŸŽฎํŒ”๋กœ์šฐ๊ธฐ๋Šฅ ( ๐Ÿ’ŽAJAX๋กœ ์ž‘๋™) , classList.toggle() , querySelectorAll()

0
post-thumbnail

๐ŸŸฆ classList.toggle()

โœ… ํด๋ž˜์Šค๊ฐ’์ด ์žˆ๋Š”์ง€ ์ฒดํฌํ•˜๊ณ  ์—†์œผ๋ฉด ๋”ํ•˜๊ณ  ์žˆ์œผ๋ฉด ์ œ๊ฑฐํ•œ๋‹ค.

๐ŸŸฆ querySelectorAll()

โœ… ํ•ด๋‹น ์„ ํƒ์ž์— ํ•ด๋‹นํ•˜๋Š” ๋ชจ๋“  ์š”์†Œ.

โœ… ๋ฐ˜ํ™˜๊ฐ์ฒด๋Š” nodeList. for๋ฌธ ๋˜๋Š” foreach ๋ฌธ์„ ์‚ฌ์šฉ.

๐ŸŸฆ MyConst

    public final String PROFILE = "profile";
    public final String PROFILE_LIST = "profileList";
    public final String RESULT = "result";
    public final String YOU_FOLLOW_ME = "youFollowMe";

๐ŸŸฆ commonHeader.html

<header th:fragment="header">
    <span id="globalConst" sec:authorize="isAuthenticated()" 
          th:with="auth=${#authentication.getPrincipal().getUser()}"
          th:attr="data-iuser=${auth.iuser}, data-writer=${auth.nm}, data-writer-profile=${auth.mainProfile}"></span>

                          ...


   <ul class="menus" sec:authorize="isAuthenticated()" th:with="auth=${#authentication.getPrincipal().getUser()}">
      <li>
          <a th:href="@{/user/profile}">
            <span class="span__profile">
               <img class="profile wh50" th:if="${auth.mainProfile != null}" 
                      th:src="@{/pic/profile/{iuser}/{img}(iuser=${auth.iuser}, img=${auth.mainProfile})}">
              <span th:text="${auth.nm}"></span>
            </span>
          </a>
      </li>
                               ...
  </ul>
  • ๋กœ๊ทธ์ธํ•œ ์œ ์ €์ธ ๊ฒฝ์šฐ
    • auth์— ์ •๋ณด๊ฐ€ ๋‹ด๊ธด๋‹ค
    • ์‚ฌ์ง„ ๋˜๋Š” ์ด๋ฆ„์„ ๋ˆ„๋ฅด๋ฉด
      /user/profile ๋‚ด ํ”„๋กœํ•„๋กœ ์ด๋™ํ•œ๋‹ค

๐ŸŸฆ feed.js

function moveToProfile(iuser) {
  location.href = `/user/profile?iuser=${iuser}`;
}
  • ํ”ผ๋“œ์—์„œ ํ”„๋กœํ•„์‚ฌ์ง„์„ ๋ˆ„๋ฅด๋ฉด
    /user/profile?iuser=${iuser} ๊ทธ ์‚ฌ๋žŒ์˜ ํ”„๋กœํ•„๋กœ ์ด๋™ํ•œ๋‹ค.

๐ŸŸฆ UserController

@GetMapping("/profile")
public void profile(Model model, UserEntity param, @AuthenticationPrincipal CustomUserPrincipal userDetails) {
    System.out.println(param);

    UserDTO param2 = new UserDTO();
    param2.setYouIuser(param.getIuser());
    if(param2.getYouIuser() == 0) {
        param2.setYouIuser(userDetails.getUser().getIuser());
        param.setIuser(userDetails.getUser().getIuser());
    }
    model.addAttribute(myConst.PROFILE, service.selUserProfile(param2));
    model.addAttribute(myConst.PROFILE_LIST, service.selUserProfileList(param));
}
  • ํŒŒ๋ผ๋ฏธํ„ฐ๋กœ ๋„˜์–ด์˜จ UserEntity์˜ iuser๊ฐ’์ด ์—†๋‹ค๋ฉด
    • ์ฆ‰, ๋‚˜์˜ ํ”„๋กœํ•„๋กœ ์ด๋™ํ•˜๋Š” ๊ฑฐ๋ผ๋ฉด
    • youIuser(UserDTO)์™€ iuser(UserEntity)๋Š” ๋‚˜์ด๋‹ค.
  • html๋กœ profile : UserDomain ๊ฐ์ฒด , profileList : List<UserProfileEntity> ๊ฐ์ฒด ๋ฅผ ๋„˜๊ฒจ์ค€๋‹ค.

๐ŸŸฆ UserSerivce

@Autowired private IAuthenticationFacade auth;

public UserDomain selUserProfile(UserDTO param) {
    param.setMeIuser(auth.getLoginUserPk());
    return profileMapper.selUserProfile(param);
}
  • UserDTO๋ฅผ ์„ธํŒ…ํ•ด์ฃผ๊ณ  mapper์˜ ๊ฒฐ๊ณผ UserDomain์˜ ๊ฐ์ฒด๋ฅผ ๋ฆฌํ„ดํ•œ๋‹ค.
    • ๋‚˜์˜ ํ”„๋กœํ•„๋กœ ์ด๋™ํ•  ๊ฒฝ์šฐ
      • YouIuser = ๋‚˜ , MeIuser = ๋‚˜
    • ๋‹ค๋ฅธ ์‚ฌ๋žŒ์˜ ํ”„๋กœํ•„๋กœ ์ด๋™ํ•  ๊ฒฝ์šฐ
      • YouIuser = ๊ทธ์‚ฌ๋žŒ , MeIuser = ๋‚˜

๐ŸŸฆ UserProfileMapper

UserDomain selUserProfile(UserDTO param);
<select id="selUserProfile" resultType="UserDomain">
  SELECT
      A.iuser, A.email, A.nm, A.tel, A.mainProfile, A.regdt
      , (SELECT COUNT(ifeed) FROM t_feed WHERE iuser = ${youIuser}) AS cntFeed
      , (SELECT COUNT(iuserMe) FROM t_user_follow WHERE iuserMe = ${youIuser}) AS cntFollow
      , (SELECT COUNT(iuserYou) FROM t_user_follow WHERE iuserYou = ${youIuser}) AS cntFollower
      , (SELECT COUNT(iuserMe) FROM t_user_follow WHERE iuserMe = ${youIuser} AND iuserYou = ${meIuser}) AS isYouFollowMe
      , (SELECT COUNT(iuserMe) FROM t_user_follow WHERE iuserMe = ${meIuser} AND iuserYou = ${youIuser}) AS isMeFollowYou
  FROM t_user A
  WHERE A.iuser = ${youIuser}
</select>
  • cntFeed

    • iuser = ๋‚ด ํ”„๋กœํ•„ : ๋‚˜ , ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : ๊ทธ์‚ฌ๋žŒ = ${youIuser}
    • ๋‚ด ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” [ ๋‚˜ ]์˜.
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” [ ๊ทธ์‚ฌ๋žŒ์˜ ] ifeed๊ฐฏ์ˆ˜๋ฅผ.
  • cntFollow

    • iuserMe = ๋‚ด ํ”„๋กœํ•„ : ๋‚˜ , ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : ๊ทธ์‚ฌ๋žŒ = ${youIuser}
    • ๋‚ด๊ฐ€ ๋‹ค๋ฅธ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐํ•œ(iuserMe) ๊ฐฏ์ˆ˜๋ฅผ (iuserMe๊ฐ€ [ ๋‚˜ ]์˜ ์ž…์žฅ์—์„œ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]์„ ํŒ”๋กœ์šฐํ•œ ๊ฒƒ์ด๋‹ค.)
    • ๋‚ด ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋‚ด๊ฐ€ [ ๋‚˜ ] , (iuserMe = ${youIuser})
    • ๋‹ค๋ฅธ ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๊ทธ์‚ฌ๋žŒ์ด [ ๋‚˜ ]
  • cntFollower

    • iuserYou = ๋‚ด ํ”„๋กœํ•„ : ๋‚˜ , ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : ๊ทธ์‚ฌ๋žŒ = ${youIuser}
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ์ด ๋‚˜๋ฅผ ํŒ”๋กœ์šฐํ•œ(iuserYou) ๊ฐฏ์ˆ˜๋ฅผ (iuserYou๊ฐ€ [ ๋‚˜ ]์˜ ์ž…์žฅ์—์„œ ๋‹ค๋ฅธ ์‚ฌ๋žŒ๋“ค์ด ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ ํ•œ ๊ฒƒ์ด๋‹ค.)
    • ๋‚ด ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋‚ด๊ฐ€ [ ๋‚˜ ] (iuserYou = ${youIuser})
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๊ทธ์‚ฌ๋žŒ์ด [ ๋‚˜ ]
  • isYouFollowMe

    • iuserMe = ๋‚ด ํ”„๋กœํ•„ : ๋‚˜ , ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : ๊ทธ์‚ฌ๋žŒ = ${youIuser}
    • iuserYou = ๋‚ด ํ”„๋กœํ•„ : ๋‚˜ , ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : ๋‚˜ = ${meIuser}
    • ๋‚ด๊ฐ€ ๋‹ค๋ฅธ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐํ•œ(iuserMe)๊ฐฏ์ˆ˜๋ฅผ (iuserMe๊ฐ€ [ ๋‚˜ ]์˜ ์ž…์žฅ์—์„œ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]๋“ค์„ ํŒ”๋กœ์šฐํ•œ ๊ฒƒ์ด๋‹ค.)
    • ๋‚ดํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋‚ด๊ฐ€ [ ๋‚˜ ] , ๋‚ด๊ฐ€ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๊ทธ์‚ฌ๋žŒ์ด [ ๋‚˜ ] , ๋‚ด๊ฐ€ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]
    • iuserMe = ${youIuser} , iuserYou = ${meIuser}
    • ๊ทธ๋ž˜์„œ ๋‚ดํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋ฌด์กฐ๊ฑด isYouFollowMe๋Š” 0์ด ๋‚˜์˜ค๊ณ ,
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” (๊ทธ์‚ฌ๋žŒ์ด [ ๋‚˜ ] , ๋‚ด๊ฐ€ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]) ๊ทธ์‚ฌ๋žŒ์ด ๋‚˜๋ฅผ ํŒ”๋กœ์šฐํ–ˆ์„ ๋•Œ๋งŒ 1์ด ๋‚˜์˜จ๋‹ค.
  • isMeFollowYou

    • ๋‚ด๊ฐ€ ๋‹ค๋ฅธ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐํ•œ(iuserMe) ๊ฐฏ์ˆ˜๋ฅผ (iuserMe๊ฐ€ [ ๋‚˜ ]์˜ ์ž…์žฅ์—์„œ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]๋“ค์„ ํŒ”๋กœ์šฐํ•œ ๊ฒƒ์ด๋‹ค.)
    • ๋‚ด ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋‚ด๊ฐ€ [ ๋‚˜ ], ๋‚ด๊ฐ€ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋‚ด๊ฐ€ [ ๋‚˜ ], ๊ทธ์‚ฌ๋žŒ์ด [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]
    • iuserMe = ${meIuser} , iuserYou = ${youIuser}
    • ๊ทธ๋ž˜์„œ ๋‚ด ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” ๋ฌด์กฐ๊ฑด isYouFollowMe์™€ isMeFollowYou๋Š” 0์ด ๋‚˜์˜ค๊ณ 
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„์„ ๋“ค์–ด๊ฐˆ ๋•Œ๋Š” (๋‚ด๊ฐ€ [ ๋‚˜ ], ๊ทธ์‚ฌ๋žŒ์ด [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]) ๋‚ด๊ฐ€ ๊ทธ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐํ–ˆ์„ ๋•Œ๋งŒ 1์ด ๋‚˜์˜จ๋‹ค.
  • cntFollow : ๋‚ด ํ”„๋กœํ•„ - ๋‚ด๊ฐ€ ํŒ”๋กœ์šฐ , ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ - ๊ทธ์‚ฌ๋žŒ์ด ํŒ”๋กœ์šฐ (iuserMe)

  • cntFollower : ๋‚ดํ”„๋กœํ•„ - ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ, ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ - ๊ทธ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ (iuserYou)

  • isYouFollowMe : ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ - ๊ทธ์‚ฌ๋žŒ์ด ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ (iuserMe = ${youIuser} , iuserYou = ${meIuser})

  • isMeFollowYou : ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ - ๋‚ด๊ฐ€ ๊ทธ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ (iuserMe = ${meIuser} , iuserYou = ${youIuser})


๐ŸŸฆ table

CREATE TABLE t_user_follow (
   iuserMe INT UNSIGNED,
   iuserYou INT UNSIGNED,
   regdt DATETIME DEFAULT NOW(),
   PRIMARY KEY(iuserMe, iuserYou),
   FOREIGN KEY (iuserMe) REFERENCES t_user(iuser),
   FOREIGN KEY (iuserYou) REFERENCES t_user(iuser)
);

๐ŸŸฆ UserFollowEntity

public class UserFollowEntity {
    private int iuserMe;
    private int iuserYou;
    private String regdt;
}

๐ŸŸฆ UserEntity

    private int iuser;
    private String provider;
    private String email;
    private String pw;
    private String nm;
    private String tel;
    private String authCd;
    private String mainProfile;
    private String regdt;

๐ŸŸฆ UserDomain

public class UserDomain extends UserEntity {
    private int cntFeed; //ํ”ผ๋“œ ์นด์šดํŠธ
    private int cntFollower; //ํŒ”๋กœ์›Œ ์นด์šดํŠธ
    private int cntFollow; //ํŒ”๋กœ์šฐ ์นด์šดํŠธ
    private int isYouFollowMe; //๋„ˆ๋Š” ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ ํ–ˆ๋‹ˆ
    private int isMeFollowYou; //๋‚˜๋Š” ๋„ˆ๋ฅผ ํŒ”๋กœ์šฐ ํ–ˆ๋‹ˆ
}

๐ŸŸฆ UserDTO

public class UserDTO {
    private int meIuser;
    private int youIuser;
}

๐ŸŸฆ profile.html

<div id="localConst" th:attr="data-iuser=${profile.iuser}"></div>

<td th:unless="${auth.iuser eq profile.iuser}">
    <th:block th:if="${profile.isMeFollowYou eq 1}">
        <input type="button" class="instaBtn instaBtnEnable" id="btnFollow" data-follow="1" value="ํŒ”๋กœ์šฐ ์ทจ์†Œ">
    </th:block>
    <th:block th:unless="${profile.isMeFollowYou eq 1}">
        <th:block th:if="${profile.isYouFollowMe eq 1}">
            <input type="button" class="instaBtn" id="btnFollow" data-follow="0" value="๋งžํŒ”๋กœ์šฐ">
        </th:block>
        <th:block th:unless="${profile.isYouFollowMe eq 1}">
            <input type="button" class="instaBtn" id="btnFollow" data-follow="0" value="ํŒ”๋กœ์šฐ">
        </th:block>
    </th:block>
</td>
  • ์ƒ๋Œ€๋ฐฉ์˜ ํŽ˜์ด์ง€
    • ๋‚ด๊ฐ€ ์ด ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ ํ–ˆ์œผ๋ฉด
      • ํŒ”๋กœ์šฐ ์ทจ์†Œ ๋ฒ„ํŠผ.
    • ๋‚ด๊ฐ€ ์ด ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ ํ•˜์ง€ ์•Š์•˜์œผ๋ฉด
      • ๋งžํŒ”๋กœ์šฐ ๋ฒ„ํŠผ. ๐Ÿ‘ˆ ๊ทผ๋ฐ ์ƒ๋Œ€๋ฐฉ์€ ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ ํ–ˆ๋‹ค๋ฉด
      • ํŒ”๋กœ์šฐ ๋ฒ„ํŠผ. ๐Ÿ‘ˆ ์ƒ๋Œ€๋ฐฉ๋„ ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ ํ•˜์ง€ ์•Š์•˜๋‹ค๋ฉด

๐ŸŸฆ profile.js

const btnFollowElem = document.querySelector('#btnFollow'); //ํŒ”๋กœ์šฐ ๋ฒ„ํŠผ

//ํŒ”๋กœ์šฐ ์ฒ˜๋ฆฌ
//type: 0 > ํŒ”๋กœ์šฐ ์ฒ˜๋ฆฌ
//type: 1 > ํŒ”๋กœ์šฐ ์ทจ์†Œ
function followProc(type, iuserYou, btnElem) {
    const init = {};
    const param = { iuserYou };
    let queryString = '';
    switch (type) {
        case 0:
            init.method = 'POST';
            init.headers = { 'Content-Type': 'application/json' };
            init.body = JSON.stringify(param);
            break;
        case 1:
            init.method = 'DELETE';
            queryString = `?iuserYou=${iuserYou}`;
            break;
    }

    fetch('follow' + queryString, init)
        .then(res => res.json())
        .then(myJson => {

            if(myJson.result === 1) {
                let buttnNm = 'ํŒ”๋กœ์šฐ ์ทจ์†Œ';
                if(btnElem.dataset.follow === '1') {
                    if(myJson.youFollowMe == null) { buttnNm = 'ํŒ”๋กœ์šฐ'; }
                    else { buttnNm = '๋งžํŒ”๋กœ์šฐ'; }
                }
                btnElem.classList.toggle('instaBtnEnable');
                btnElem.value = buttnNm;
                btnElem.dataset.follow = 1 - btnElem.dataset.follow;
            } else {
                alert('์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.');
            }
        });
}

if(btnFollowElem) {
    btnFollowElem.addEventListener('click', () => {
        const follow = parseInt(btnFollowElem.dataset.follow);
        const iuser = localConstElem.dataset.iuser;

        followProc(follow, iuser, btnFollowElem);
    });
}

๐ŸŸฆ UserController

    @ResponseBody
    @PostMapping("/follow")
    public Map<String, Object> doFollow(@RequestBody UserFollowEntity param) {
        return service.insUserFollow(param);
    }

    @ResponseBody
    @DeleteMapping("/follow")
    public Map<String, Object> cancelFollow(UserFollowEntity param) {
        return service.delUserFollow(param);
    }
  • UserFollowEntity , iuserYou : ๋‹ค๋ฅธ ์‚ฌ๋žŒ ํ”„๋กœํ•„(๊ทธ์‚ฌ๋žŒ)

๐ŸŸฆ UserService

 //ํŒ”๋กœ์šฐ ํ•˜๊ธฐ
    public Map<String, Object> insUserFollow(UserFollowEntity param) {
        param.setIuserMe(auth.getLoginUserPk());
        Map<String, Object> res = new HashMap();
        res.put(myConst.RESULT, mapper.insUserFollow(param));
        return res;
    }

//ํŒ”๋กœ์šฐ ์ทจ์†Œ
    public Map<String, Object> delUserFollow(UserFollowEntity param) {
        param.setIuserMe(auth.getLoginUserPk());
        int result = mapper.delUserFollow(param);

        Map<String, Object> res = new HashMap();
        res.put(myConst.RESULT, result);
        if(result == 1) {
            UserFollowEntity param2 = new UserFollowEntity();
            param2.setIuserMe(param.getIuserYou());
            param2.setIuserYou(param.getIuserMe());

            UserFollowEntity result2 = mapper.selUserFollow(param2);
            res.put(myConst.YOU_FOLLOW_ME, result2);
        }
        return res;
    }
  • insUserFollow :

    • UserFollowEntity , iuserYou : ๋‹ค๋ฅธ ์‚ฌ๋žŒ ํ”„๋กœํ•„(๊ทธ์‚ฌ๋žŒ) , iuserMe : ๋‚˜
    • ๋‚ด๊ฐ€ ๊ทธ ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ ํ•œ๋‹ค.
    • Map<String, Object> res : "result" , 0 ๋˜๋Š” 1
  • delUserFollow :

    • UserFollowEntity , iuserYou : ๋‹ค๋ฅธ ์‚ฌ๋žŒ ํ”„๋กœํ•„(๊ทธ์‚ฌ๋žŒ) , iuserMe : ๋‚˜
    • ๋‚ด๊ฐ€ ๊ทธ ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐ ํ•œ๋‹ค.
    • UserFollowEntity , iuserYou : ๋‚˜ , iuserMe : ๋‹ค๋ฅธ ์‚ฌ๋žŒ ํ”„๋กœํ•„(๊ทธ์‚ฌ๋žŒ)
    • ๊ทธ ์‚ฌ๋žŒ์ด ๋‚˜๋ฅผ ํŒ”๋กœ์šฐ ํ•œ๋‹ค.
    • Map<String, Object> res :
      • "result" , 0 ๋˜๋Š” 1
      • "youFollowMe" , null ๋˜๋Š” UserFollowEntity(iuserYou : ๋‚˜ , iuserMe : ๋‹ค๋ฅธ ์‚ฌ๋žŒ ํ”„๋กœํ•„(๊ทธ์‚ฌ๋žŒ))

๐ŸŸฆ UserMapper

int insUserFollow(UserFollowEntity param);
UserFollowEntity selUserFollow(UserFollowEntity param);
int delUserFollow(UserFollowEntity param);

    <insert id="insUserFollow">
        INSERT INTO t_user_follow
        ( iuserMe, iuserYou )
        VALUES
        ( ${iuserMe}, ${iuserYou} )
    </insert>

    <delete id="delUserFollow">
        DELETE from t_user_follow
        WHERE iuserMe = ${iuserMe} AND iuserYou = ${iuserYou}
    </delete>

    <select id="selUserFollow" resultType="UserFollowEntity">
        SELECT iuserMe, iuserYou, regdt FROM t_user_follow
        WHERE iuserMe = ${iuserMe}
        AND iuserYou = ${iuserYou}
    </select>
  • insUserFollow : ( ${iuserMe} : ๋‚˜ , ${iuserYou} : ๊ทธ์‚ฌ๋žŒ )
  • delUserFollow : ( ${iuserMe} : ๋‚˜ , ${iuserYou} : ๊ทธ์‚ฌ๋žŒ )
  • insUserFollow : ( ${iuserMe} : ๊ทธ์‚ฌ๋žŒ , ${iuserYou} : ๋‚˜ )

๐ŸŸฆ ํŒ”๋กœ์šฐ ๊ธฐ๋Šฅ ๋กœ์ง (ํŒ”๋กœ์šฐ ๋ฒ„ํŠผ์„ ๋ˆŒ๋ €์„ ๋•Œ)

  • header์—์„œ ๋˜๋Š” feed์—์„œ ํ”„๋กœํ•„๊ฐ€๊ธฐ๋ฅผ ๋ˆ„๋ฅธ๋‹ค.

    • ๋‚ด ํ”„๋กœํ•„ : /user/profile
    • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : /user/profile?iuser=${iuser}

    ๐Ÿ‘‡ @controller

  • ๋‚ด ํ”„๋กœํ•„ : UserDTO.youIuser = ๋‚˜

  • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : UserDTO.youIuser = ๊ทธ์‚ฌ๋žŒ

    ๐Ÿ‘‡ @service

  • ๋‚ด ํ”„๋กœํ•„ : UserDTO.meIuser = ๋‚˜ , youIuser = ๋‚˜

  • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„ : UserDTO.meIuser = ๋‚˜ , youIuser = ๊ทธ์‚ฌ๋žŒ

    ๐Ÿ‘‡ @mapper

  • ๋‚ด ํ”„๋กœํ•„

    • UserDomain.๋‚˜์˜
    • iuser , email , nm , tel , mainProfile , regdt , cntFeed , cntFollow , cntFollower
    • isYouFollowMe(0) , isMeFollowMe(0)
  • ๋‹ค๋ฅธ์‚ฌ๋žŒ ํ”„๋กœํ•„

    • UserDomain.๊ทธ์‚ฌ๋žŒ์˜
    • iuser , email , nm , tel , mainProfile , regdt , cntFeed , cntFollow , cntFollower
    • isYouFollowMe , isMeFollowMe

    ๐Ÿ‘‡ @controller

  • "profile" , UserDomain

    ๐Ÿ‘‡ profile.html

    ๐Ÿ‘‡ profile.js - ๋‚ด ํ”„๋กœํ•„์ผ๊ฒฝ์šฐ ์—ฌ๊ธฐ๋กœ ์˜ฌ ์ˆ˜ ์—†๋‹ค.

  • btnFollowElem.dataset.follow : 0(ํŒ”๋กœ์šฐ) , 1(ํŒ”๋กœ์šฐ์ทจ์†Œ)

  • localConstElem.dataset.iuser : profile.iuser(๊ทธ์‚ฌ๋žŒ)

  • followProc( follow, iuser, btnFollowElem)

    • follow (0-@Post /user/follow | 1-@Delete /user/follow)
    • iuser (profile.iuser(๊ทธ์‚ฌ๋žŒ) = UserFollowEntity.iuserYou)
  • ํŒ”๋กœ์šฐ์ฒ˜๋ฆฌ : "result" , 1

    • btnNm = 'ํŒ”๋กœ์šฐ์ทจ์†Œ'
    • classList = instaBtn instaBtnEnable
    • dataset.follow = 1
    • <input type="button" class="instaBtn" id="btnFollow" data-follow="0" value="(๋งž)ํŒ”๋กœ์šฐ">
      โ†“
    • <input type="button" class="instaBtn instaBtnEnable" id="btnFollow" data-follow="1" value="ํŒ”๋กœ์šฐ ์ทจ์†Œ">
  • ํŒ”๋กœ์šฐ์ทจ์†Œ์ฒ˜๋ฆฌ : "result" , 1 | "youFollowMe" , null ๋˜๋Š” UserFollowEntity(iuserMe ๊ทธ์‚ฌ๋žŒ , iuserYou ๋‚˜)

    • btnNm = 'ํŒ”๋กœ์šฐ์ทจ์†Œ' ๐Ÿ‘‰ 'ํŒ”๋กœ์šฐ' | '๋งžํŒ”๋กœ์šฐ'
    • classList = instaBtn
    • dataset.follow = 0
    • <input type="button" class="instaBtn instaBtnEnable" id="btnFollow" data-follow="1" value="ํŒ”๋กœ์šฐ ์ทจ์†Œ">
      โ†“
    • <input type="button" class="instaBtn" id="btnFollow" data-follow="0" value="(๋งž)ํŒ”๋กœ์šฐ">

๐ŸŸฆ profile.html

<header th:fragment="header">
    <span id="globalConst" sec:authorize="isAuthenticated()" th:with="auth=${#authentication.getPrincipal().getUser()}"
          th:attr="data-iuser=${auth.iuser}, data-writer=${auth.nm}, data-writer-profile=${auth.mainProfile}"></span>

<div id="localConst" th:attr="data-iuser=${profile.iuser}"></div>

    <td class="pointer follower">ํŒ”๋กœ์›Œ</td>
    <td class="pointer follower" th:text="${profile.cntFollower}"></td>
    <td class="pointer follow">ํŒ”๋กœ์šฐ</td>
    <td class="pointer follow" th:text="${profile.cntFollow}"></td>

<div class="modal-follow hide">
    <div>
        <div class="container">
            <div class="top">
                <div id="title">ํŒ”๋กœ์šฐ</div>
                <i id="modal-follow-close" class="moodal_clse fas fa-times"></i>
            </div>
            <div class="followCont"></div>
        </div>
    </div>
</div>
  • ํŒ”๋กœ์šฐ , ํŒ”๋กœ์šฐ ์ˆ˜ ๋˜๋Š” ํŒ”๋กœ์›Œ , ํŒ”๋กœ์›Œ ์ˆ˜๋ฅผ ๋ˆ„๋ฅด๋ฉด ์‚ฌ์šฉ์ž๋ฆฌ์ŠคํŠธ ๋ชจ๋‹ฌ์ฐฝ์ด ๋œฌ๋‹ค.

๐ŸŸฆ profile.js

const followerElemArr = document.querySelectorAll('.pointer.follower');
const followElemArr = document.querySelectorAll('.pointer.follow');
const modalFollowElem = document.querySelector('.modal-follow');
const modalFollowTitleElem = modalFollowElem.querySelector('#title');
const modalFollowCloseElem = document.querySelector('.modal-follow #modal-follow-close');
const modalFollowItemConElem = modalFollowElem.querySelector('.followCont');

if(followerElemArr) {
    followerElemArr.forEach(item => {
        item.addEventListener('click', () => {
            modalFollowTitleElem.innerText = 'ํŒ”๋กœ์›Œ';
            modalFollowElem.classList.remove('hide');
            modalFollowItemConElem.innerHTML = '';

            //ํ”„๋กœํ•„ ์‚ฌ์šฉ์ž๋ฅผ ํŒ”๋กœ์šฐํ•œ ์‚ฌ๋žŒ๋“ค ๋ฆฌ์ŠคํŠธ
            fetch(`getFollowerList?iuserYou=${localConstElem.dataset.iuser}`)
                .then(res => res.json())
                .then(myJson => {
                    if(myJson.length > 0) {
                        myJson.forEach(item => {
                            const cont = makeFollowItem(item);
                            modalFollowItemConElem.append(cont);
                        });
                    }
                });
        });
    });
}

if(followElemArr) {
    followElemArr.forEach(item => {
        item.addEventListener('click', () => {
            modalFollowTitleElem.innerText = 'ํŒ”๋กœ์šฐ';
            modalFollowElem.classList.remove('hide');
            modalFollowItemConElem.innerHTML = '';

            //ํ”„๋กœํ•„ ์‚ฌ์šฉ์ž๊ฐ€ ํŒ”๋กœ์šฐํ•œ ์‚ฌ๋žŒ๋“ค ๋ฆฌ์ŠคํŠธ
            fetch(`getFollowList?iuserYou=${localConstElem.dataset.iuser}`)
                .then(res => res.json())
                .then(myJson => {
                    if(myJson.length > 0) {
                        myJson.forEach(item => {
                            const cont = makeFollowItem(item);
                            modalFollowItemConElem.append(cont);
                        });
                    }
                });
        });
    });
}

if(modalFollowCloseElem) {
    modalFollowCloseElem.addEventListener('click', () => {
        modalFollowElem.classList.add('hide');
    });
}

function makeFollowItem(item) {
    const globalContElem = document.querySelector('#globalConst');
    const loginIuser = globalContElem.dataset.iuser;

    const cont = document.createElement('div');
    cont.className = 'follow-item';
    const img = document.createElement('img');
    img.className = 'profile wh30 pointer';
    img.src = `/pic/profile/${item.iuser}/${item.mainProfile}`;
    img.onerror = () => { img.style.visibility = 'hidden'; }
    img.addEventListener('click', ()=> {
        moveToProfile(item.iuser); //feed.js ์— ์žˆ๋Š” ๋ฉ”์†Œ๋“œ
    });

    const nm = document.createElement('div');
    const nmText = document.createElement('span');
    nmText.innerText = item.nm;
    nmText.className = 'pointer';
    nmText.addEventListener('click', ()=> {
        moveToProfile(item.iuser); //feed.js ์— ์žˆ๋Š” ๋ฉ”์†Œ๋“œ
    });
    nm.append(nmText);

    const btn = document.createElement('input');
    btn.className = 'instaBtn pointer';
    btn.dataset.follow = '0';
    btn.addEventListener('click', () => {
        const follow = parseInt(btn.dataset.follow);
        followProc(follow, item.iuser, btn);
    });

    cont.append(img);
    cont.append(nm);
    if(parseInt(loginIuser) !== item.iuser) {
        btn.type = 'button';
        if(item.isMeFollowYou) {
            btn.dataset.follow = '1';
            btn.value = 'ํŒ”๋กœ์šฐ ์ทจ์†Œ';
        } else {
            btn.classList.add('instaBtnEnable');
            btn.value = 'ํŒ”๋กœ์šฐ';
        }
        cont.append(btn);
    }
    return cont;
}
  • ์Šคํฌ๋ฆฐ์ƒท(18)
  • followerElemArr : <td class="pointer follower">ํŒ”๋กœ์›Œ</td><td class="pointer follower" th:text="${profile.cntFollower}"></td>
  • followElemArr : <td class="pointer follow">ํŒ”๋กœ์šฐ</td><td class="pointer follow" th:text="${profile.cntFollow}"></td>
  • modalFollowElem :
  •  <div class="modal-follow hide">
         <div>
             <div class="container">
                 <div class="top">
                     <div id="title">ํŒ”๋กœ์šฐ</div>
                     <i id="modal-follow-close" class="moodal_clse fas fa-times"></i>
                 </div>
                 <div class="followCont"></div>
             </div>
         </div>
     </div>
  • modalFollowTitleElem : <div id="title">ํŒ”๋กœ์šฐ</div>
  • modalFollowCloseElem : <i id="modal-follow-close" class="moodal_clse fas fa-times"></i>
  • modalFollowItemConElem : <div class="followCont"></div>

๐ŸŸฆ UserController

@ResponseBody
@GetMapping("/getFollowerList")
public List<UserDomain> getFollowerList(UserFollowEntity param) {
    return service.selUserFollowerList(param);
}

@ResponseBody
@GetMapping("/getFollowList")
public List<UserDomain> getFollowList(UserFollowEntity param) {
    return service.selUserFollowList(param);
}

๐ŸŸฆ UserService

public List<UserDomain> selUserFollowerList(UserFollowEntity param) {
    param.setIuserMe(auth.getLoginUserPk());
    return mapper.selUserFollowerList(param);
}

public List<UserDomain> selUserFollowList(UserFollowEntity param) {
    param.setIuserMe(auth.getLoginUserPk());
    return mapper.selUserFollowList(param);
}

๐ŸŸฆ UserMapper

List<UserDomain> selUserFollowerList(UserFollowEntity param);
List<UserDomain> selUserFollowList(UserFollowEntity param);
<select id="selUserFollowerList" resultType="UserDomain">
    SELECT B.iuser, B.nm, B.mainProfile
         , CASE WHEN C.iuserMe IS NULL THEN 0 ELSE 1 END AS isMeFollowYou
    FROM t_user_follow A
    INNER JOIN t_user B
    ON A.iuserMe = B.iuser
    LEFT JOIN t_user_follow C
    ON C.iuserMe = ${iuserMe} -- ๋กœ๊ทธ์ธํ•œ ์‚ฌ๋žŒ pk
    AND C.iuserYou = A.iuserMe
    WHERE A.iuserYou = ${iuserYou} -- ํ”„๋กœํ•„ ์ฃผ์ธ์žฅ  pk
</select>

<select id="selUserFollowList" resultType="UserDomain">
    SELECT B.iuser, B.nm, B.mainProfile
    , CASE WHEN C.iuserMe IS NULL THEN 0 ELSE 1 END AS isMeFollowYou
    FROM t_user_follow A
    INNER JOIN t_user B
    ON A.iuserYou = B.iuser
    LEFT JOIN t_user_follow C
    ON C.iuserMe = ${iuserMe} -- ๋กœ๊ทธ์ธํ•œ ์‚ฌ๋žŒ pk
    AND C.iuserYou = A.iuserYou
    WHERE A.iuserMe = ${iuserYou} -- ํ”„๋กœํ•„ ์ฃผ์ธ์žฅ  pk
</select>

๐ŸŸฆ ํŒ”๋กœ์›Œ ๋ฆฌ์ŠคํŠธ ๋กœ์ง

profile.js followerElemArr.forEach(item => { item.addEventListener('click', () => {})});

  • getFollowList?iuserYou=${localConstElem.dataset.iuser}
    • ${localConstElem.dataset.iuser} = ${profile.iuser} ๐Ÿ‘‰ [ ๋‚˜ ] ๋˜๋Š” [ ๊ทธ์‚ฌ๋žŒ ]์˜ iuser

๐Ÿ‘‡

UserController. getFollowerList()

@ResponseBody
@GetMapping("/getFollowerList")
public List<UserDomain> getFollowerList(UserFollowEntity param) {
    return service.selUserFollowerList(param);
}
  • UserFollowEntity.iuserYou(๊ทธ์‚ฌ๋žŒ ๋˜๋Š” ๋‚˜)

๐Ÿ‘‡

UserService.selUserFollowerList()

public List<UserDomain> selUserFollowerList(UserFollowEntity param) {
    param.setIuserMe(auth.getLoginUserPk());
    return mapper.selUserFollowerList(param);
}
  • UserFollowEntity.iuserYou(๊ทธ์‚ฌ๋žŒ ๋˜๋Š” ๋‚˜) | iuserMe(๋‚˜)

๐Ÿ‘‡

UserMapper.selUserFollowerList()

<select id="selUserFollowerList" resultType="UserDomain">
    SELECT B.iuser, B.nm, B.mainProfile
         , CASE WHEN C.iuserMe IS NULL THEN 0 ELSE 1 END AS isMeFollowYou
    FROM t_user_follow A
    INNER JOIN t_user B
    ON A.iuserMe = B.iuser
    LEFT JOIN t_user_follow C
    ON C.iuserMe = ${iuserMe} -- ๋กœ๊ทธ์ธํ•œ ์‚ฌ๋žŒ pk
    AND C.iuserYou = A.iuserMe
    WHERE A.iuserYou = ${iuserYou} -- ํ”„๋กœํ•„ ์ฃผ์ธ์žฅ  pk
</select>
  • iuserMe๊ฐ€ [ ๋‚˜ ]์˜ ์ž…์žฅ์—์„œ [ ๋‹ค๋ฅธ์‚ฌ๋žŒ ]์„ ํŒ”๋กœ์šฐ ํ•œ ๊ฒƒ์ด๋‹ค.
  • A.iuserMe = B.iuser๋Š” A.iuserYou = ${iuserYou} : [๊ทธ์‚ฌ๋žŒ ๋˜๋Š” ๋‚˜]๋ฅผ ํŒ”๋กœ์šฐํ•œ ์‚ฌ๋žŒ๋“ค์ด๋‹ค.
  • C.iuserMe = ${iuserMe} : [ ๋‚˜ ]
  • ์ฆ‰, C.iuserMe = ${iuserMe} [ ๋‚˜ ]์ธ๋ฐ, A.iuserYou = ${iuserYou} [ ๊ทธ ์‚ฌ๋žŒ ๋˜๋Š” ๋‚˜ ]๋ฅผ ํŒ”๋กœ์šฐํ•˜๋Š” A.iuserMe = B.iuser = C.iuserYou๋ฅผ ํŒ”๋กœ์šฐํ•˜๋Š” ์ง€ ์•Œ์•„๋ณด๋Š” ๊ฒƒ์ด๋‹ค.

๐Ÿ‘‡

profile.js makeFollowItem(item)

  • item : [ ๊ทธ์‚ฌ๋žŒ ๋˜๋Š” ๋‚˜ ]๋ฅผ ํŒ”๋กœ์šฐํ•˜๋Š” ์‚ฌ๋žŒ์˜ iuser, nm, mainprofile , iuserMe ([ ๋‚ด ] ๊ฐ€ [๊ทธ์‚ฌ๋žŒ ๋˜๋Š” ๋‚˜]๋ฅผ ํŒ”๋กœ์šฐํ•˜๋Š” ์‚ฌ๋žŒ์„ ํŒ”๋กœ์šฐํ•˜๋Š”์ง€)
    const globalContElem = document.querySelector('#globalConst');
    const loginIuser = globalContElem.dataset.iuser;
  • globalContElem : <span id="globalConst" sec:authorize="isAuthenticated()" th:with="auth=${#authentication.getPrincipal().getUser()}" th:attr="data-iuser=${auth.iuser}, data-writer=${auth.nm}, data-writer-profile=${auth.mainProfile}"></span>
  • loginIuser : [ ๋‚˜ ]
    const cont = document.createElement('div');
    cont.className = 'follow-item';
  • <div class="follow-item"></div>
    const img = document.createElement('img');
    img.className = 'profile wh30 pointer';
    img.src = `/pic/profile/${item.iuser}/${item.mainProfile}`;
    img.onerror = () => { img.style.visibility = 'hidden'; }
    img.addEventListener('click', ()=> {
        moveToProfile(item.iuser); //feed.js ์— ์žˆ๋Š” ๋ฉ”์†Œ๋“œ
    });
  • <img class="profile wh30 pointer" src="/pic/profile/${item.iuser}/${item.mainProfile}">
    const nm = document.createElement('div');
    const nmText = document.createElement('span');
    nmText.innerText = item.nm;
    nmText.className = 'pointer';
    nmText.addEventListener('click', ()=> {
        moveToProfile(item.iuser); //feed.js ์— ์žˆ๋Š” ๋ฉ”์†Œ๋“œ
    });
    nm.append(nmText);
  •     <div>
            <span class="pointer" onclick="moveToProfile(item.iuser)">item.nm</span>
        </div>
    const btn = document.createElement('input');
    btn.type = 'button';
    btn.className = 'instaBtn pointer';
    btn.dataset.follow = '0';
    btn.addEventListener('click', () => {
        const follow = parseInt(btn.dataset.follow);
        followProc(follow, item.iuser, btn);
    });
  • <input type="button" class="instaBtn pointer" data-follow="0">
    cont.append(img);
    cont.append(nm);
  • <div class="follow-item">
        <img class="profile wh30 pointer" src="/pic/profile/${item.iuser}/${item.mainProfile}" 
                onerror="" onclick="moveToProfile(item.iuser)">
        <div>
              <span class="pointer" onclick="moveToProfile(item.iuser)">item.nm</span>
        </div>
    </div>
    if(parseInt(loginIuser) !== item.iuser) {
        if(item.isMeFollowYou) {
            btn.classList.add('instaBtnEnable');
            btn.dataset.follow = '1';
            btn.value = 'ํŒ”๋กœ์šฐ ์ทจ์†Œ';
        } else {
            
            btn.value = 'ํŒ”๋กœ์šฐ';
        }
        cont.append(btn);
    }
    return cont;
}
  • if(parseInt(loginIuser) !== item.iuser) : [ ๋‚˜ ] ์™€ [ ๋‚˜ ๋˜๋Š” ๊ทธ์‚ฌ๋žŒ ]์„ ํŒ”๋กœ์šฐํ•˜๋Š” ์‚ฌ๋žŒ์ด ๊ฒน์นœ๋‹ค๋ฉด ,
    ์ฆ‰, [ ๊ทธ ์‚ฌ๋žŒ ]์„ ํŒ”๋กœ์šฐํ•˜๋Š” ์‚ฌ๋žŒ ์ค‘์— [ ๋‚ด ]๊ฐ€ ์žˆ๋‹ค๋ฉด ํŒ”๋กœ์šฐ ๋ฒ„ํŠผ์„ ๋„ฃ์ง€ ์•Š๊ฒ ๋‹ค.
  • <div class="follow-item">
        <img class="profile wh30 pointer" src="/pic/profile/${item.iuser}/${item.mainProfile}" 
                onerror="" onclick="moveToProfile(item.iuser)">
        <div>
              <span class="pointer" onclick="moveToProfile(item.iuser)">item.nm</span>
        </div>
        <input type="button" class="instaBtn pointer" data-follow="0"
                 value="ํŒ”๋กœ์šฐ" onclick=" followProc(follow, item.iuser, btn);">
        <input type="button" class="instaBtn instaBtnEnable pointer" data-follow="1"
                 value="ํŒ”๋กœ์šฐ ์ทจ์†Œ" onclick=" followProc(follow, item.iuser, btn);">
    </div>

๐Ÿ‘‡

profile.js followerElemArr.forEach(item => { item.addEventListener('click', () => {})});

........

modalFollowTitleElem.innerText = 'ํŒ”๋กœ์›Œ';
modalFollowElem.classList.remove('hide');
modalFollowItemConElem.innerHTML = '';

........

    .then(myJson => {
        if(myJson.length > 0) {
            myJson.forEach(item => {
                const cont = makeFollowItem(item);
                modalFollowItemConElem.append(cont);
            });
        }

๐Ÿ‘‡ ๊ฒฐ๊ณผ

 <div class="modal-follow">
     <div>
         <div class="container">
             <div class="top">
                 <div id="title">ํŒ”๋กœ์›Œ</div>
                 <i id="modal-follow-close" class="moodal_clse fas fa-times"></i>
             </div>
             <div class="followCont">
                <div class="follow-item">
                    <img class="profile wh30 pointer" src="/pic/profile/${item.iuser}/${item.mainProfile}" 
                            onerror="" onclick="moveToProfile(item.iuser)">
                    <div>
                          <span class="pointer" onclick="moveToProfile(item.iuser)">item.nm</span>
                    </div>
                    <input type="button" class="instaBtn pointer" data-follow="0"
                             value="ํŒ”๋กœ์šฐ" onclick=" followProc(follow, item.iuser, btn);">
                    <input type="button" class="instaBtn instaBtnEnable pointer" data-follow="1"
                             value="ํŒ”๋กœ์šฐ ์ทจ์†Œ" onclick=" followProc(follow, item.iuser, btn);">
              </div>
          </div>
         </div>
     </div>
 </div>
profile
๋ช‡ ๋ฒˆ์„ ๋„˜์–ด์ ธ๋„ ์•ž์œผ๋กœ ๊ณ„์† ๋‚˜์•„๊ฐ€์ž

0๊ฐœ์˜ ๋Œ“๊ธ€