The re-entrancy attack is when a user call your contract and that your contract then make an external call that could potentially come back in the contract.
Here is a simple example;
function withdraw(uint256 amount) public {
require(balances[msg.sender]> amount, "Insuficient balance");
token.transfer(amount, msg.sender);
balances[msg.sender] = balances[msg.sender] - amount;
}
Now imagine that the msg.sender is a contract. The contract can make a call to your contract withdraw function when it receives token. This would create a loop and the balance will not get adjusted because when your contract execute the token transfer, the caller contract immediately call withdraw again.
This is why we always make external call at last.
In the example, the balance would never get adjusted so the user can keep re entering your contract and withdraw some more funds. Until the contract is empty.
There are plenty of example out there on how to avoid it.